第三次pta大作业
OO第三次博客作业
一、PTA三次大作业题目集的前言
对于第六次,第七次,第八次pta大作业,难度不是很难,主要是考察对输入数据的处理、应用和储存。
1.第六次题目集
7-1 电信计费系列1-座机计费:
实现一个简单的电信计费程序:假设南昌市电信分公司针对市内座机用户采用的计费方式,是对数据进行识别,储存,处理,计算,最后在输出电话号码和花费以及余额;
7-2 多态测试:
定义容器Container接口。模拟实现一个容器类层次结构,并进行接口的实现、抽象方法重写和多态机制测试。各容器类实现求表面积、体积的方法。运用抽象方法来进行对输入数据的处理,在进行运算。
2.第七次题目集
7-1 电信计费系列2-手机+座机计费:
实现南昌市电信分公司的计费程序,假设该公司针对手机和座机用户分别采取了两种计费方案,分别如下:对数据进行识别,储存,处理,计算,最后在输出电话号码和花费以及余额
7-2 sdut-Collection-sort--C~K的班级(II):
经过不懈的努力,C~K终于当上了班主任。
现在他要统计班里学生的名单,但是C~K在教务系统中导出班级名单时出了问题,发现会有同学的信息重复,现在他想把重复的同学信息删掉,只保留一个,
但是工作量太大了,所以找到了会编程的你,你能帮他解决这个问题吗?
排序:
String p=null;
for(int i=0;i<s;i++) {
for(int k=i+1;k<s;k++) {
if(q[i].compareTo(q[k])>0){
p=q[i];
q[i]=q[k];
q[k]=p;
}
}
}
7-3 阅读程序,按照题目需求修改程序:
功能需求:
使用集合存储3个员工的信息(有序);
通过迭代器依次找出所有的员工。
3.第八次题目集
7-1 电信计费系列3-短信计费:
实现一个简单的电信计费程序,针对手机的短信采用如下计费方式:
7-2 编写一个类Shop(商店)、内部类InnerCoupons(内部购物券):
编写一个类Shop(商店),该类中有一个成员内部类InnerCoupons(内部购物券),可以用于购买该商店的牛奶(假设每箱牛奶售价为50元)。要求如下:
7-3 动物发声模拟器(多态):
设计一个动物发生模拟器,用于模拟不同动物的叫声。比如狮吼、虎啸、狗旺旺、猫喵喵……。
二、设计与分析
1.7-1 电信计费系列1-座机计费
实现一个简单的电信计费程序:
假设南昌市电信分公司针对市内座机用户采用的计费方式:
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
南昌市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
输入格式:
输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码除区号外由是7-8位数字组成。
本题只考虑计费类型0-座机计费,电信系列2、3题会逐步增加计费类型。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
注意:
本题非法输入只做格式非法的判断,不做内容是否合理的判断(时间除外,否则无法计算),比如:
1、输入的所有通讯信息均认为是同一个月的通讯信息,不做日期是否在同一个月还是多个月的判定,直接将通讯费用累加,因此月租只计算一次。
2、记录中如果同一电话号码的多条通话记录时间出现重合,这种情况也不做判断,直接 计算每条记录的费用并累加。
3、用户区号不为南昌市的区号也作为正常用户处理。
输出格式:
根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,
单位元)。假设每个用户初始余额是100元。
每条通讯信息单独计费后累加,不是将所有时间累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
建议类图:
参见图1、2、3,可根据理解自行调整:

图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。

图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。
CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。

图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是
座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
后续扩展说明:
后续题目集将增加手机用户,手机用户的计费方式中除了与座机计费类似的主叫通话费之外,还包含市外接听电话的漫游费以及发短信的费用。在本题的设计时可统一考虑。
通话记录中,手机需要额外记录拨打/接听的地点的区号,比如:
座机打手机:t-主叫号码 接听号码 接听地点区号 起始时间 结束时间
t-079186330022 13305862264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
手机互打:t-主叫号码 拨号地点 接听号码 接听地点区号 起始时间 结束时间
t-18907910010 0791 13305862264 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
短信的格式:m-主叫号码,接收号码,短信内容
m-18907910010 13305862264 welcome to jiangxi
m-13305862264 18907910010 thank you
部分参考测试用例: 以下内容绿色部分为输入,紧接其后的黑色部分为输出。
u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25
end
079186300001 3.0 77.0
u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:26
end
079186300001 3.6 76.4
u-079186300001 0
u-079186300002 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25
t-079186300002 058686330022 2022.1.3 11:00:25 2022.1.3 11:05:25
t-079186300001 079186300001 2022.1.3 12:00:25 2022.1.3 12:05:25
end
079186300001 3.5 76.5
079186300002 3.0 77.0
u-079186300002 0
u-079186300001 0
t-079186300001 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:25 t-079186300002 058686330022 2022.1.3 11:00:25 2022.1.3 11:05:25
t-079186300001 079186300001 2022.1.3 12:00:25 2022.1.3 12:05:25 end 079186300001 3.5 76.5 079186300002 3.0 77.0
u-079186300002 0
u-079186300001 0
t-079186300001 079286300002 2022.1.3 10:00:25 2022.1.3 10:05:25 end 079186300001 1.5 78.5 079186300002 0.0 80.0 u-079186300002 0
u-079186300001 0
t-079186300001 079286300002 2022.1.3 10:00:25 2022.1.3 10:05:25
t-079186300001 079186300003 2022.1.3 10:00:25 2022.1.3 10:05:25
end
079186300001 2.0 78.0
079186300002 0.0 80.0
u-079186300002 0
u-079186300001 0
t-079186300001 079186300002 2022.1.3 22:00:25 2022.1.3 22:05:32 t-079186300001 079186300003 2022.1.3 10:00:25 2022.1.3 10:05:25
end
079186300001 1.1 78.9
079186300002 0.0 80.0
u-079186300001 0
t-079186300001 079286330022 2022.1.3 12:50:00 2022.1.3 13:05:00 22
rr
end
079186300001 4.5 75.
2.7-2 多态测试
定义容器Container接口。模拟实现一个容器类层次结构,并进行接口的实现、抽象方法重写和多态机制测试。各容器类实现求表面积、体积的方法。
- 定义接口Container:
属性:
public static final double pi=3.1415926;
抽象方法:
public abstract double area();
public abstract double volume();
static double sumofArea(Container c[]);
static double sumofVolume(Container c[]);
其中两个静态方法分别计算返回容器数组中所有对象的面积之和、周长之和; - 定义Cube类、Cylinder类均实现自Container接口。
Cube类(属性:边长double类型)、Cylinder类(属性:底圆半径、高,double类型)。
输入格式:
第一行n表示对象个数,对象类型用cube、cylinder区分,cube表示立方体对象,后面输入边长,输入cylinder表示圆柱体对象,后面是底圆半径、高。
输出格式:
分别输出所有容器对象的表面积之和、体积之和,结果保留小数点后2位。
import java.text.DecimalFormat; import java.util.Scanner; public class Main { public static void main(String[] args) { String str; Scanner sc=new Scanner(System.in); int n=sc.nextInt(); Container c[] = new Container[n]; for(int i=0;i<n;i++) { str=sc.next(); if(str.equals("cube")){ c[i]=new Cube(sc.nextDouble()); } else if(str.equals("cylinder")) { double q=sc.nextDouble(); double p=sc.nextDouble(); c[i]=new Cylinder(q,p); } } System.out.println(Container.sumofArea(c)); System.out.println(Container.sumofVolume(c)); } } interface Container{ public static final double pi=3.1415926; public abstract double area(); public abstract double volume(); static double sumofArea(Container c[]) { double s=0; for(Container j:c) { s+=j.area(); } DecimalFormat d = new DecimalFormat("#.00"); Double output = Double.valueOf(d.format(s)); return output; } static double sumofVolume(Container c[]) { double f=0; for(Container d:c) { f+=d.volume(); } DecimalFormat d = new DecimalFormat("#.00"); Double output = Double.valueOf(d.format(f)); return output; } } class Cube implements Container{ private double q; Cube(double q) { this.q=q; } public double area() { double a=q*q*6; return a; } public double volume() { double v=q*q*q; return v; } } class Cylinder implements Container{ private double q; private double p; Cylinder(double q, double p) { this.q=q; this.p=p; } public double area() { double a=pi*q*q*2+2*pi*q*p; return a; } public double volume() { double v=pi*q*q*p; return v; } }
输入样例:
在这里给出一组输入。例如:
4
cube
15.7
cylinder
23.5 100
cube
46.8
cylinder
17.5 200
输出样例:
在这里给出相应的输出。例如:
56771.13
472290.12
3.7.1 电信计费系列2-手机+座机计费
实现南昌市电信分公司的计费程序,假设该公司针对手机和座机用户分别采取了两种计费方案,分别如下:
1、针对市内座机用户采用的计费方式(与电信计费系列1内容相同):
月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
假设本市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
2、针对手机用户采用实时计费方式:
月租15元,市内省内接电话均免费,市内拨打市内电话0.1元/分钟,市内拨打省内电话0.2元/分钟,市内拨打省外电话0.3元/分钟,省内漫游打电话0.3元/分钟,省外漫游接听0.3元/分钟,省外漫游拨打0.6元/分钟;
注:被叫电话属于市内、省内还是国内由被叫电话的接听地点区号决定,比如以下案例中,南昌市手机用户13307912264在区号为020的广州接听了电话,主叫号码应被计算为拨打了一个省外长途,同时,手机用户13307912264也要被计算省外接听漫游费:
u-13307912264 1
t-079186330022 13307912264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
输入:
输入信息包括两种类型
1、逐行输入南昌市用户开户的信息,每行一个用户,含手机和座机用户
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐)
例如:u-079186300001 0
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题在电信计费系列1基础上增加类型1-手机实时计费。
手机设置0或者座机设置成1,此种错误可不做判断。
2、逐行输入本月某些用户的通讯信息,通讯信息格式:
座机呼叫座机:t-主叫号码 接听号码 起始时间 结束时间
t-079186330022 058686330022 2022.1.3 10:00:25 2022.1.3 10:05:11
以上四项内容之间以一个英文空格分隔,
时间必须符合"yyyy.MM.dd HH:mm:ss"格式。提示:使用SimpleDateFormat类。
输入格式增加手机接打电话以及收发短信的格式,手机接打电话的信息除了号码之外需要额外记录拨打/接听的地点的区号,比如:
座机打手机:
t-主叫号码 接听号码 接听地点区号 起始时间 结束时间
t-079186330022 13305862264 020 2022.1.3 10:00:25 2022.1.3 10:05:11
手机互打:
t-主叫号码 拨号地点 接听号码 接听地点区号 起始时间 结束时间
t-18907910010 0791 13305862264 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细通讯信息,计算所有已开户的用户的当月费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条通讯、短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。
建议类图:
参见图1、2、3:

图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。

图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。CallRecord(通话记录类)包含属性:
通话的起始、结束时间以及
拨号地点的区号(callingAddressAreaCode)、接听地点的区号(answerAddressAreaCode)。
区号用于记录在哪个地点拨打和接听的电话。座机无法移动,就是本机区号,如果是手机号,则会有差异。

图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList<CallRecord> callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
注意:以上图中所定义的类不是限定要求,根据实际需要自行补充或修改。
import java.util.ArrayList; import java.util.Date; import java.text.SimpleDateFormat; import java.util.Scanner; public class Main { public static void main(String[] args) { SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); ArrayList<User> Users=new ArrayList<User>(); Scanner sc=new Scanner(System.in); String mq=sc.nextLine(); mq=mq.substring(2); String[] s=mq.split(" "); User user; while(mq.charAt(0)=='u') { if((mq.length()==14||mq.length()==13)) { user=new User(); user.number=s[0]; Users.add(user); } else if((mq.length()==14||mq.length()==13)) { user=new User(); user.number=s[0]; Users.add(user); } mq=sc.nextLine(); s=mq.split(" "); } while(mq.charAt(0)=='t') { int m=0; for(int i=0;i<mq.length();i++) { if(mq.charAt(i)==' ') { m++; } } if(m==7) { } mq=mq.substring(2); if(mq.length()==13||mq.length()==1) { user=new User(); user.number=s[0]; Users.add(user); } } mq=sc.nextLine(); s=mq.split(" "); } } class User{ String number; double balance=100; // UserRecords chargeMode; double buy=0; ArrayList<conversation> All = new ArrayList<conversation>(); } class UserRecords { String callnumber; String getnumber; } class conversation extends UserRecords { Date time1; Date time2; String callplace; String getplace; } class format{ public static boolean iszuoji(String zuoji) { return true; } public static boolean isshouji(String phone) { return true; } public static SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); public static boolean time(String time) { try { format.parse(time); return true; }catch(Exception e){ return false; } } }
u-13811111111 1
u-13811111110 1
u-13811111111 1
t-13811111111 0791 13811111110 020 2022.1.3 08:00:00 2022.1.3 08:09:20
end
13811111110 3.0 82.0
13811111111 3.0 82.0
u-079186300001 0
u-13986300001 1
t-18907910010 0791 13986300001 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
end
079186300001 0.0 80.0
13986300001 1.5 83.5
u-079186300001 0
u-13986300001 1
t-079186300001 13986300001 0371 2022.1.3 10:00:25 2022.1.3 10:05:11
end
079186300001 3.0 77.0
13986300001 1.5 83.5
u-079186300001 0
u-13986300001 1
t-13986300001 0371 079186300001 2022.1.3 10:00:25 2022.1.3 10:05:11
end
079186300001 0.0 80.0
13986300001 3.0 82.0
u-079186300001 0
u-13986300001 1
t-13986300001 0371 079186300001 2022.1.3 23:50:25 2022.1.4 00:05:11
end
079186300001 0.0 80.0
13986300001 9.0 76.0
u-13986300001 1
t-13986300001 0791 079186300001 2022.12.31 23:50:25 2023.1.2 00:05:11
end
13986300001 145.5 -60.5
u-13986300001 1
t-13986300001 0791 079186300001 2022.12.31 23:50:25 2023.1.1 00:05:11
t-13986300001 0799 079186300001 2022.12.31 23:50:25 2023.1.1 00:05:11
t-13986300001 0391 079186300001 2022.12.31 23:50:25 2023.1.1 00:05:11
end
13986300001 15.0 70.0
u-13986300001 1
u-079182200000 0
u-079182200000 0
u-13986300066 1
t-079182200000 13986300066 0730 2022.8.31 23:50:25 2022.9.1 00:00:11 ss
t-079182200000 13986300066 0791 2022.8.31 23:50:25 2022.9.1 00:00:11
t-079182200000 0730000000 2022.8.31 23:50:25 2022.9.1 00:00:11
t-13986300066 0730 13986300001 0731 2022.8.31 23:50:25 2022.9.1 00:00:11
end
079182200000 13.0 67.0
13986300001 3.0 82.0
13986300066 9.0 76.
4.7-2 sdut-Collection-sort--C~K的班级(II)
经过不懈的努力,C~K终于当上了班主任。
现在他要统计班里学生的名单,但是C~K在教务系统中导出班级名单时出了问题,发现会有同学的信息重复,现在他想把重复的同学信息删掉,只保留一个,
但是工作量太大了,所以找到了会编程的你,你能帮他解决这个问题吗?
输入格式:
第一行输入一个N,代表C~K导出的名单共有N行(N<100000).
接下来的N行,每一行包括一个同学的信息,学号 姓名 年龄 性别。
输出格式:
第一行输出一个n,代表删除重复名字后C~K的班级共有几人。
接下来的n行,输出每一个同学的信息,输出按照学号从小到大的顺序。
import java.util.Scanner; public class Main { public static void main(String[] args) { String str; Scanner in = new Scanner(System.in); int s = in.nextInt(); in.nextLine(); String[] q = new String[s]; for(int i=0;i<s;i++) { str=in.nextLine(); q[i]=str; } for(int i=0;i<s-1;i++) { for(int k=i+1;k<s;k++) { if(q[i].equals(q[k])) { for(int j=k;j<s-1;j++) q[j]=q[j+1]; s--; k--; } } } System.out.println(s); String p=null; for(int i=0;i<s;i++) { for(int k=i+1;k<s;k++) { if(q[i].compareTo(q[k])>0){ p=q[i]; q[i]=q[k]; q[k]=p; } } } for(int i=0;i<s;i++) { System.out.println(q[i]); } } }
输入样例:
6
0001 MeiK 20 M
0001 MeiK 20 M
0002 sdk2 21 M
0002 sdk2 21 M
0002 sdk2 21 M
0000 blf2 22 F
输出样例:
3
0000 blf2 22 F
0001 MeiK 20 M
0002 sdk2 21 M
5.7-3 阅读程序,按照题目需求修改程序
功能需求:
使用集合存储3个员工的信息(有序);
通过迭代器依次找出所有的员工。
提示:学生复制以下代码到编程区,并按需求进行调试修改。
// 1、导入相关包 //定义员工类 class Employee { private String name; private int age; public Employee() { super(); } public Employee(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } //主函数 public class Main { public static void main(String[] args) { // 1、创建有序集合对象 Collection c ; // 创建3个员工元素对象 for (int i = 0; i < 3; i++) { Scanner sc = new Scanner(System.in); String employeeName = sc.nextLine(); int employeeAge = sc.nextInt(); Employee employee = new Employee(employeeName, employeeAge); c.add(employee); } // 2、创建迭代器遍历集合 Iterator it; //3、遍历 while (it.hasnext) { //4、集合中对象未知,向下转型 Employee e = it.next(); System.out.println(e.getName() + "---" + e.getAge()); } } }
//1、导入相关包 import java.util.ArrayList; import java.util.Scanner; import java.util.Collection; import java.util.Iterator; //定义员工类 class Employee { private String name; private int age; public Employee() { super(); } public Employee(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } //主函数 public class Main { public static void main(String[] args) { // 1、创建有序集合对象 Collection c = new ArrayList(); Scanner sc = new Scanner(System.in); String employeeName = sc.nextLine(); int employeeAge = sc.nextInt(); // 创建3个员工元素对象 for (int i = 0; i < 3; i++) { Employee employee = new Employee(employeeName, employeeAge); c.add(employee); } // 2、创建迭代器遍历集合 Iterator it=c.iterator(); //3、遍历 while (it.hasNext()) { //4、集合中对象未知,向下转型 Employee e = (Employee)it.next(); System.out.println(e.getName() + "---" + e.getAge()); } } }
输入样例:
在这里给出一组输入。例如:
zs
10
ls
20
ww
30
输出样例:
在这里给出相应的输出。例如:
zs---10
ls---20
ww---30
6.7-1 电信计费系列3-短信计费
实现一个简单的电信计费程序,针对手机的短信采用如下计费方式:
1、接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。
2、如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。
输入:
输入信息包括两种类型
1、逐行输入南昌市手机用户开户的信息,每行一个用户。
格式:u-号码 计费类型 (计费类型包括:0-座机 1-手机实时计费 2-手机A套餐 3-手机短信计费)
例如:u-13305862264 3
座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。
手机号码由11位数字构成,最高位是1。
本题只针对类型3-手机短信计费。
2、逐行输入本月某些用户的短信信息,短信的格式:
m-主叫号码,接收号码,短信内容 (短信内容只能由数字、字母、空格、英文逗号、英文句号组成)
m-18907910010 13305862264 welcome to jiangxi.
m-13305862264 18907910010 thank you.
注意:以上两类信息,先输入所有开户信息,再输入所有通讯信息,最后一行以“end”结束。
输出:
根据输入的详细短信信息,计算所有已开户的用户的当月短信费用(精确到小数点后2位,单位元)。假设每个用户初始余额是100元。
每条短信信息均单独计费后累加,不是将所有信息累计后统一计费。
格式:号码+英文空格符+总的话费+英文空格符+余额
每个用户一行,用户之间按号码字符从小到大排序。
错误处理:
输入数据中出现的不符合格式要求的行一律忽略。
本题只做格式的错误判断,无需做内容上不合理的判断,比如同一个电话两条通讯记录的时间有重合、开户号码非南昌市的号码、自己给自己打电话等,此类情况都当成正确的输入计算。但时间的输入必须符合要求,比如不能输入2022.13.61 28:72:65。
本题只考虑短信计费,不考虑通信费用以及月租费。
建议类图:
参见图1、2、3:

图1
图1中User是用户类,包括属性:
userRecords (用户记录)、balance(余额)、chargeMode(计费方式)、number(号码)。
ChargeMode是计费方式的抽象类:
chargeRules是计费方式所包含的各种计费规则的集合,ChargeRule类的定义见图3。
getMonthlyRent()方法用于返回月租(monthlyRent)。
UserRecords是用户记录类,保存用户各种通话、短信的记录,
各种计费规则将使用其中的部分或者全部记录。
其属性从上到下依次是:
市内拨打电话、省内(不含市内)拨打电话、省外拨打电话、
市内接听电话、省内(不含市内)接听电话、省外接听电话的记录
以及发送短信、接收短信的记录。

图2
图2中CommunicationRecord是抽象的通讯记录类:
包含callingNumber拨打号码、answerNumber接听号码两个属性。
CallRecord(通话记录)、MessageRecord(短信记录)是它的子类。

图3
图3是计费规则的相关类,这些类的核心方法是:
calCost(ArrayList callRecords)。
该方法针根据输入参数callRecords中的所有记录计算某用户的某一项费用;如市话费。
输入参数callRecords的约束条件:必须是某一个用户的符合计费规则要求的所有记录。
SendMessageRule是发送短信的计费规则类,用于计算发送短信的费用。
LandPhoneInCityRule、LandPhoneInProvinceRule、LandPhoneInLandRule三个类分别是座机拨打市内、省内、省外电话的计费规则类,用于实现这三种情况的费用计算。
(提示:可以从UserRecords类中获取各种类型的callRecords)。
注意:以上图中所定义的类不是限定要求,根据实际需要自行补充或修改。
import java.util.ArrayList; import java.util.Scanner; import java.text.DecimalFormat; import java.util.Comparator; public class Main { public static void main(String[] args) { Scanner sc=new Scanner(System.in); String mq=sc.nextLine(); ArrayList<User> Users=new ArrayList<User>(); DecimalFormat df = new DecimalFormat("0.00"); User user; Xiaoxi xiaoxi; String nr; String hm2; while(!mq.equals("end")) { if(mq.matches("u-1[0-9]{10} 3")) { user=new User(); user.number=mq.substring(2,13); Users.add(user); } else if(mq.matches("m-1[0-9]{10} 1[0-9]{10} [a-z A-Z 0-9,.]+")) { hm2=mq.substring(2,13); for(User xd: Users) { if(hm2.equals(xd.number)) { xiaoxi=new Xiaoxi(); xiaoxi.number1=mq.substring(2,13); xiaoxi.number2=mq.substring(14,25); nr=mq.substring(26); xiaoxi.isneirong=nr; if(Xiaoxi.isduanxin(nr)) { for(User a:Users) { if(a.number.equals(xiaoxi.number1)) a.All.add(xiaoxi); } } } } } else {} mq=sc.nextLine(); } int sum=0; for(User a:Users) { for(Xiaoxi b:a.All) { double n1=b.isneirong.length()/10.0; int n2=(int) n1; if(n1-n2>0) n2++; sum+=n2; } if(sum>5) a.buy+=0.7+(sum-5)*0.3; else if(sum>3) a.buy+=0.3+(sum-3)*0.2; else a.buy+=sum*0.1; a.balance-=a.buy; sum=0; } Users.sort(new Sort_length()); for(User a:Users) { System.out.println(a.number+" "+Double.valueOf(df.format(a.buy))+" "+Double.valueOf(df.format(a.balance))); } } } class Sort_length implements Comparator<User>{ public int compare(User a,User b) { return a.number.compareTo(b.number); } } class Number{ public static boolean isdianhua(String haoma) { if(haoma.matches("u-1[0-9]{10} 3")) { return true; } return false; } } class Xiaoxi{ public String isneirong; String number1; String number2; public static boolean isduanxin(String xinxi) { if(xinxi.matches("[0-9 a-z A-Z,.]+")) { return true; } return false; } } class User{ String number; double balance=100; double buy=0; ArrayList<Xiaoxi> All = new ArrayList<Xiaoxi>(); }
输入样例:
在这里给出一组输入。例如:
u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaaaaaaaaaaaaa
end
输出样例:
在这里给出相应的输出。例如:
18907910010 0.3 99.7
### 输入样例1:
在这里给出一组输入。例如:
u-18907910010 3
m-18907910010 13305862264 aaaaaaaaaaaa
m-18907910010 13305862264 aaaaaaa.
m-18907910010 13305862264 bb,bbbb
end
输出样例1:
在这里给出相应的输出。例如:
18907910010 0.5 99.5
7.7-2 编写一个类Shop(商店)、内部类InnerCoupons(内部购物券)
编写一个类Shop(商店),该类中有一个成员内部类InnerCoupons(内部购物券),可以用于购买该商店的牛奶(假设每箱牛奶售价为50元)。要求如下:
(1)Shop类中有私有属性milkCount(牛奶的箱数,int类型)、公有的成员方法setMilkCount( )和getMilkCount( )分别用于设置和获取牛奶的箱数。
(2)成员内部类InnerCoupons,有公有属性value(面值,int类型),一个带参数的构造方法可以设定购物券的面值value,一个公有的成员方法buy( )要求输出使用了面值为多少的购物券进行支付,同时使商店牛奶的箱数减少value/50。
(3)Shop类中还有成员变量coupons50(面值为50元的内部购物券,类型为InnerCoupons)、coupons100(面值为100元的内部购物券,类型为InnerCoupons)。
(4)在Shop类的构造方法中,调用内部类InnerCoupons的带参数的构造方法分别创建上面的购物券coupons50、coupons100。
在测试类Main中,创建一个Shop类的对象myshop,从键盘输入一个整数(大于或等于3),将其设置为牛奶的箱数。假定有顾客分别使用了该商店面值为50的购物券、面值为100的购物券各消费一次,分别输出消费后商店剩下的牛奶箱数。
输入格式:
输入一个大于或等于3的整数。
输出格式:
使用了面值为50的购物券进行支付
牛奶还剩XX箱
使用了面值为100的购物券进行支付
牛奶还剩XX箱
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc=new Scanner(System.in); Shop myshop=new Shop(sc.nextInt()); Shop.InnerCoupons coupons50=myshop.new InnerCoupons(50); Shop.InnerCoupons coupons100=myshop.new InnerCoupons(100); coupons50.buy(); coupons100.buy(); } } class Shop{ static int milkCount; InnerCoupons coupons50; InnerCoupons coupons100; public Shop(int milkCount) { this.milkCount=milkCount; } public void setMilkCount(int milkCount) { this.milkCount=milkCount; } public int getMilkCount() { return milkCount; } public class InnerCoupons{ public int value; public InnerCoupons(int value){ this.value=value; } public void buy(){ Shop.this.milkCount=Shop.this.milkCount-value/50; System.out.println("使用了面值为"+value+"的购物券进行支付"); System.out.println("牛奶还剩"+Shop.this.milkCount+"箱"); } } }
输入样例:
在这里给出一组输入。例如:
5
输出样例:
在这里给出相应的输出。例如:
使用了面值为50的购物券进行支付
牛奶还剩4箱
使用了面值为100的购物券进行支付
牛奶还剩2箱
8.7-3 动物发声模拟器(多态)
设计一个动物发生模拟器,用于模拟不同动物的叫声。比如狮吼、虎啸、狗旺旺、猫喵喵……。
定义抽象类Animal,包含两个抽象方法:获取动物类别getAnimalClass()、动物叫shout();
然后基于抽象类Animal定义狗类Dog、猫类Cat和山羊Goat,用getAnimalClass()方法返回不同的动物类别(比如猫,狗,山羊),用shout()方法分别输出不同的叫声(比如喵喵、汪汪、咩咩)。
最后编写AnimalShoutTest类测试,输出:
猫的叫声:喵喵
狗的叫声:汪汪
山羊的叫声:咩咩
其中,在AnimalShoutTestMain类中,用speak(Animal animal){}方法输出动物animal的叫声,在main()方法中调用speak()方法,分别输出猫、狗和山羊对象的叫声。
请在下面的【】处添加代码。
//动物发生模拟器. 请在下面的【】处添加代码。 public class AnimalShoutTest2 { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); Goat goat = new Goat(); speak(cat); speak(dog); speak(goat); } //定义静态方法speak() 【】 } //定义抽象类Animal 【】class Animal{ 【】 } //基于Animal类,定义猫类Cat,并重写两个抽象方法 class Cat 【】{ 【】 【】 } //基于Animal类,定义狗类Dog,并重写两个抽象方法 class Dog 【】{ 【】 【】 } //基于Animal类,定义山羊类Goat,并重写两个抽象方法 class Goat 【】{ 【】 【】 }
public class Main { public static void main(String[] args) { Cat cat = new Cat(); Dog dog = new Dog(); Goat goat = new Goat(); speak(cat); speak(dog); speak(goat); } //定义静态方法speak() public static void speak(Animal animal) { if(animal instanceof Cat) { Cat cat = new Cat(); cat.shout(); } if(animal instanceof Dog) { Dog dog = new Dog(); dog.shout(); } if(animal instanceof Goat) { Goat goat = new Goat(); goat.shout(); } } } //定义抽象类Animal abstract class Animal{ public abstract Animal getAnimalClass(); public abstract void shout(); } //基于Animal类,定义猫类Cat,并重写两个抽象方法 class Cat extends Animal{ public Animal getAnimalClass() { Animal cat = new Cat(); return cat; } public void shout() { System.out.println("猫的叫声:喵喵"); } } //基于Animal类,定义狗类Dog,并重写两个抽象方法 class Dog extends Animal{ public Animal getAnimalClass() { Animal dog = new Dog(); return dog; } public void shout() { System.out.println("狗的叫声:汪汪"); } } //基于Animal类,定义山羊类Goat,并重写两个抽象方法 class Goat extends Animal{ public Animal getAnimalClass() { Animal goat = new Goat(); return goat; } public void shout() { System.out.println("山羊的叫声:咩咩"); } }
输入样例:
输出样例:
猫的叫声:喵喵
狗的叫声:汪汪
山羊的叫声:咩咩
三、踩坑心得
有很多种可能的输入错误及特殊情况,需要注意小心
四、改进建议
无建议
五、总结
面向对象的特点
1 继承
一种联结类的层次模型,并且允许和鼓励类的重用,提供一种明确表达共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),原始类称为新类的基类(父类)。派生类可以从它的父类哪里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。因此可以说,继承为了重用父类代码,同时为实现多态性作准备。
2 封装
封装是面向对象的特征之一,是对象和类概念的主要特性。封装就是把过程和数据包围起来,对数据的访问只能通过已定义的界面。隐藏复杂的特点,使交互变得更简单。
封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。
3多态
多态是指允许不同类的对象对同一消息做出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活/抽象/行为共享/代码共享的优势,使程序拥有更强的动态扩展能力
ArrayList常用方法总结
List接口常用方法:
1、add(Object element): 向列表的尾部添加指定的元素。
2、size(): 返回列表中的元素个数。
3、get(int index): 返回列表中指定位置的元素,index从0开始。
4、add(int index, Object element): 在列表的指定位置插入指定元素。
5、set(int i, Object element): 将索引i位置元素替换为元素element并返回被替换的元素。
6、clear(): 从列表中移除所有元素。
7、isEmpty(): 判断列表是否包含元素,不包含元素则返回 true,否则返回false。
8、contains(Object o): 如果列表包含指定的元素,则返回 true。
9、remove(int index): 移除列表中指定位置的元素,并返回被删元素。
10、remove(Object o): 移除集合中第一次出现的指定元素,移除成功返回true,否则返回false。
11、iterator(): 返回按适当顺序在列表的元素上进行迭代的迭代器。
ArrayList一样可以使用List的所有方法,所以以ArrayList来演示:
方法使用:
1、add(Object element) 向列表的尾部添加指定的元素。
2、size() 返回列表中的元素个数。
3、get(int index) 返回列表中指定位置的元素,index从0开始。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); //<String>泛型表示集合中存的是字符串形式的元素。 list.add("徐冰"); //add()用于向List集合容器中添加元素。 list.add("萤火"); System.out.println(list); System.out.println(list.size()); //size()用于获取集合中有多少个元素。 System.out.println(list.get(1)); //get()获取指定索引(从0开始)位置的元素。 } }
输出:
[徐冰, 萤火]
2
萤火
4、add(int index, Object element) 在列表的指定位置(从0开始)插入指定元素。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); System.out.println(list); list.add(1, "阿若"); //在指定位置添加元素,原来位置的元素后置。 System.out.println(list); } }
输出:
[徐冰, 萤火]
[徐冰, 阿若, 萤火]
5、set(int i, Object element) 使用元素element替换索引i位置的元素,并返回被替换的元素。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); System.out.println(list); list.set(1, "阿若"); //替换指定位置的元素,从0开始,替换了“萤火”。 System.out.println(list); } }
输出:
[徐冰, 萤火]
[徐冰, 阿若]
6、clear() 从列表中移除所有元素。
7、isEmpty() 判断列表是否包含元素,不包含元素则返回 true,否则返回false。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); System.out.println(list); list.clear(); //清空List集合中的所有元素。 System.out.println(list.isEmpty()); //集合容器没有元素,则true。 System.out.println(list); } }
输出:
[徐冰, 萤火]
true
[]
8、contains(Object o) 如果列表包含指定的元素,则返回 true。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); boolean flag = list.contains("徐冰"); //用来判断集合容器中是否含有参数元素。 System.out.println(flag); } }
这个方法需要思考一下,因为contains的参数类型是Object型,看一下源代码:
public boolean contains(Object o) { return indexOf(o) >= 0; } public int indexOf(Object o) { //因为我们设置的实参是String类型的变量,所以此时o为上转型对象。 if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) //因为o为上转型对象,所以equals表面上是Object类的,但实际上我们使用的是String类的equals方法。 return i; } return -1; }
即list为上转型对象,使用String类的equals方法比较的是两个元素的内容是否相同。
9、remove(int index) 移除列表中指定位置的元素,并返回被删元素,删除位置后面的元素(如果有)向前移动。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); list.add("阿若"); System.out.println(list.remove(1)); //删除指定索引(从0开始)位置的元素,并将元素返回,并后面元素前移。 System.out.println(list); } }
输出:
萤火
[徐冰, 阿若]
10、remove(Object o) 从List集合中移除第一次出现的指定元素,移除成功返回true,否则返回false。当且仅当List集合中含有满足(o==null ? get(i)==null : o.equals(get(i)))条件的最低索引i的元素时才会返回true。
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); list.add("阿若"); System.out.println(list.remove("萤火")); //删除List集合元素,返回boolean,并后面元素前移。 System.out.println(list); } }
输出:
true
[徐冰, 阿若]
11、iterator() 返回按适当顺序在列表的元素上进行迭代的迭代器。
例如运用iterator方法进行List遍历:
import java.util.ArrayList; import java.util.Iterator; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); Iterator<String> ite = list.iterator(); //将List集合中元素转到iterator变量中。 while(ite.hasNext()) //hasNext()判断当前“指针”下面是否还有元素的方法,java中没有指针,这只是举个例子。 { System.out.println(ite.next()); //如果“指针”下面有元素,则移动“指针”并获取相应位置的元素。 } } }
输出:
徐冰
萤火
12、for循环遍历List集合:
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); for (int i = 0; i < list.size(); i++) { //用size方法获取长度。 System.out.println(list.get(i)); //用get方法获取值。 } } }
输出:
徐冰
萤火
13、加强for循环遍历List集合:
例如:
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("徐冰"); list.add("萤火"); for (String name : list) { //使用foreach进行遍历。 System.out.println(name); } } }
输出:
徐冰
萤火
抽象类和接口_接口interface
1.接口技术,这种技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口,并在需要接口的地方随时使用实现了相应接口的对象.
2.在Java程序设计语言中,接口不是类,而是一组对类的需求描述.接口中所有方法都自动地属于public.因此在接口中声明方法是,不必提供关键字public.
3.接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如 C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
4.为什么需要接口?接口和抽象类的区别
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全 面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
5.接口格式
是 public 或默认。
接口名:和类名采用相同命名机制。
extends:接口可以多继承。
常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
方法:接口中的方法只能是:public abstract。 省略的话,也是 public abstract。
6.要点
子类通过 implements 来实现接口中的规范。
接口不能创建实例,但是可用于声明引用变量类型。
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public 的。
JDK1.8(不含 8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
JDK1.8(含 8)后,接口中包含普通的静态方法、默认方法。
抽象类和接口_静态方法和默认方法
1.JAVA8 之前,接口里的方法要求全部是抽象方法。
2.JAVA8(含 8)之后,以后允许在接口里定义默认方法和类方法(静态方法)。
默认方法
3.Java 8 及以上新版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
4.静态方法
JAVA8 以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属于接口(接口也是类,一种特殊的类),可以通过接口名调用。
如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。 可以通过子类名直接调用。
抽象类和接口_多继承
接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所定义的一切
抽象类和接口_面向接口编程
-
面向接口编程是面向对象编程的一部分。
2.为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更多的体现在具体实现上。我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋 大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现 规范的高质量的项目。
3.接口就是规范,就是项目中最稳定的核心! 面向接口编程可以让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。
4.通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高 整个系统的可扩展性和和可维护性。
都摘抄于CSDN

浙公网安备 33010602011771号