一、前言
由于接近期末,我们学习的知识也已经差不多全部完结了,所以我在此对整个学期的知识与pta最后三次作业的知识进行总结。总的来说,后三次作业集是集大成之作,无论是题目难度还是题目量都相较之前都有很大的提高,特别是有关电话计费问题的题目,无论是对我们的编程能力还是结构设计能力都有很大的提高,只不过哦。如果老师还是这样接下去教我们的话,未来会变成什么我不知道,我只知道我在这个学期中所有的知识都是由我自己学来的,和老师好不好,和是不是省级一流课程,和这个学校,和这个学科是不是很高级没有半毛钱关系,都是我自己学来的,所以我觉得这样就可以了。
二、设计与分析
1、题目集08:
总体上来说这是电信计费的起点,所以在类的设计上需要非常小心,不过总的来说为了遵守程序的简洁设计原则,每个类中的属性和方法都不会太多,总的来说都遵守以下的设计,以下是这个题目集乃至之后题目集的总类图:
以下是其中的一些代码(因为实在是太多了):
class UserRecords{ ArrayList<CallRecord> CallingInCityRecords = new ArrayList<CallRecord>(); ArrayList<CallRecord> CallingInProvinceRecords = new ArrayList<CallRecord>(); ArrayList<CallRecord> CallingInLandRecords = new ArrayList<CallRecord>(); public void addCallingInCityRecords(CallRecord callRecord) { CallingInCityRecords.add(callRecord); } public void addCallingInProvinceRecords(CallRecord callRecord) { CallingInProvinceRecords.add(callRecord); } public void addCallingInLandRecords(CallRecord callRecord) { CallingInLandRecords.add(callRecord); } public ArrayList<CallRecord> getCallingInCityRecords() { return CallingInCityRecords; } public void setCallingInCityRecords(ArrayList<CallRecord> callingInCityRecords) { CallingInCityRecords = callingInCityRecords; } public ArrayList<CallRecord> getCallingInProvinceRecords() { return CallingInProvinceRecords; } public void setCallingInProvinceRecords(ArrayList<CallRecord> callingInProvinceRecords) { CallingInProvinceRecords = callingInProvinceRecords; } public ArrayList<CallRecord> getCallingInLandRecords() { return CallingInLandRecords; } public void setCallingInLandRecords(ArrayList<CallRecord> callingInLandRecords) { CallingInLandRecords = callingInLandRecords; } }
以上的是userrecord类的代码,其中利用了链表的泛型存储各个对象的所有信息。其中大部分类之间的联系是抽象继承。以下是计算在不同区域的电信计费方式的不同类之间的计算。类图和代码如下:
abstract class ChargeRule{ public abstract double CalCost(ArrayList<CallRecord> callRecords) ; } abstract class CallChargeRule extends ChargeRule{ public double CalCost(ArrayList<CallRecord> callRecords) { return 0; } } class LandPhoneInCityRule extends CallChargeRule{ @Override public double CalCost(ArrayList<CallRecord> callRecords) { double t = 0; for(CallRecord a : callRecords) { double t1 = a.getStartTime().getTime(); double t2 = a.getEndTime().getTime(); t = t+Math.ceil(((t2-t1)/1000.0/60.0))*0.1; } return t; } } class LandPhoneInLandRule extends CallChargeRule{ public double CalCost(ArrayList<CallRecord> callRecords) { double t = 0; for(CallRecord a : callRecords) { double t1 = a.getStartTime().getTime(); double t2 = a.getEndTime().getTime(); t = t+Math.ceil(((t2-t1)/1000.0/60.0))*0.6; } return t; } } class LandPhoneInProvinceRule extends CallChargeRule{ public double CalCost(ArrayList<CallRecord> callRecords) { double t = 0; for(CallRecord a : callRecords) { double t1 = a.getStartTime().getTime(); double t2 = a.getEndTime().getTime(); t = t+Math.ceil(((t2-t1)/1000.0/60.0))*0.3; } return t; } }
其中还利用了SimpleDateFormat类的方法,这样在计算两个相隔日期之间的时间时就可以更加准确。
接下来是有关数据的输入与收取问题:
所以正则表达式的编写是非常重要的,不仅在这个题目集在后面两个题目集中也是必须的。题目集08的要求是以下代码的解释:(正则表达式太长,只有后面的部分)
TreeMap<String,User> treeMap = new TreeMap<String,User>(); while(!a.equals("end")) { String[] s1 = a.split(" "); if(a.matches(b)) { String s2 = s1[0].substring(6); ChargeMode chargeMode = new LandlinePhoneCharging(); User user=new User(s2,chargeMode); treeMap.put(s2,user); } if(a.matches(c)) { String[] e = a.split("-"); String[] f = e[1].split(" "); String s3 = s1[0].substring(2,6); String s4 = s1[1].substring(0,4); String s5 = f[0].substring(4); String time1 = s1[2]+" "+s1[3]; String time2 = s1[4]+" "+s1[5]; String q = "079[0-9]|0701"; Date starttime = new Date(); Date endtime = new Date(); SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); if(treeMap.get(s5)!=null){ try { starttime = sdf3.parse(time1);//把特点的字符串转成Date的对象 } catch (ParseException e2) { e2.printStackTrace(); } try { endtime = sdf3.parse(time2); } catch (ParseException e1) { e1.printStackTrace(); } CallRecord cal = new CallRecord(starttime,endtime,s3,s4); if(s4.equals(s3)) { treeMap.get(s5).getUserRecords().addCallingInCityRecords(cal); } else if(s4.matches(q)) { treeMap.get(s5).getUserRecords().addCallingInProvinceRecords(cal); } else { treeMap.get(s5).getUserRecords().addCallingInLandRecords(cal); } } } a = in.nextLine(); } for(User user:treeMap.values()) { System.out.println("0791"+user.getNumber()+" "+user.calCost()+" "+user.calBalance()); }
这就是数据的录入,计算的部分都由后面的类进行。
2、题目集09
不同之处其实只是多加了几个继承的类:
class AnswerPhone extends CallChargeRule{ @Override public double CalCost(ArrayList<CallRecord> callRecords) { double s = 0; for(CallRecord a : callRecords) { s = s + 0.3*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } return s; } }
class CellPhoneCity extends CallChargeRule{ @Override public double CalCost(ArrayList<CallRecord> callRecords) { // TODO 自动生成的方法存根 double s = 0; for(CallRecord a : callRecords) { if(a.getCallingAddressAreaCode().equals("0791")) { s = s + 0.1*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } if(a.getCallingAddressAreaCode().matches("079[2-9]|0701|0790")) { s = s + 0.3*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } else if(!a.getCallingAddressAreaCode().equals("0791")&&!a.getCallingAddressAreaCode().matches("079[0-9]|0701")){ s = s + 0.6*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } } return s; } } class CellPhoneLand extends CallChargeRule{ @Override public double CalCost(ArrayList<CallRecord> callRecords) { // TODO 自动生成的方法存根 double s = 0; for(CallRecord a : callRecords) { if(a.getCallingAddressAreaCode().equals("0791")) { s = s + 0.3*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } if(a.getCallingAddressAreaCode().matches("079[2-9]|0701|0790")) { s = s + 0.3*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } else if(!a.getCallingAddressAreaCode().equals("0791")&&!a.getCallingAddressAreaCode().matches("079[0-9]|0701")){ s = s + 0.6*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } } return s; } } class CellPhoneProvince extends CallChargeRule{ @Override public double CalCost(ArrayList<CallRecord> callRecords) { // TODO 自动生成的方法存根 double s = 0; for(CallRecord a : callRecords) { if(a.getCallingAddressAreaCode().equals("0791")) { s = s + 0.2*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } if(a.getCallingAddressAreaCode().matches("079[2-9]|0701|0790")) { s = s + 0.3*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } else if(!a.getCallingAddressAreaCode().equals("0791")&&!a.getCallingAddressAreaCode().matches("079[0-9]|0701")){ s = s + 0.6*Math.ceil((a.getEndTime().getTime()-a.getStartTime().getTime())/1000.0/60.0); } } return s; } }
然后就是数据之间的输入与计算:
TreeMap<String,User> treeMap = new TreeMap<String,User>(); while(!a.equals("end")) { String[] s1 = a.split(" "); if(a.matches(b)) { String[] c = a.split("-"); String[] d = c[1].split(" "); ChargeMode chargeMode = new LandlinePhoneCharging(); User user=new User(d[0],chargeMode); treeMap.put(d[0],user); } if(a.matches(b1)) { String[] e = a.split("-"); String[] f = e[1].split(" "); ChargeMode chargeMode = new CellPhoneCharging(); User user=new User(f[0],chargeMode); treeMap.put(f[0],user); } if(a.matches(s3)){ //zj zj String[] e = a.split("-"); String[] f = e[1].split(" "); String stime=f[2]+" "+f[3]; String etime=f[4]+" "+f[5]; Date starttime = new Date(); Date endtime = new Date(); SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); try { starttime = sdf3.parse(stime); } catch (ParseException e2) { e2.printStackTrace(); } try { endtime = sdf3.parse(etime); } catch (ParseException e1) { e1.printStackTrace(); } CallRecord cal = new CallRecord(starttime,endtime,f[0].substring(0, 4),f[1].substring(0, 4)); if(treeMap.get(f[0])!=null&&treeMap.size()!=0){ if(f[1].substring(0, 4).equals("0791")) { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInCityRecords(cal);} } else if(f[1].substring(0,4).matches(q)) { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInProvinceRecords(cal);} } else { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInLandRecords(cal);} } } } if(a.matches(s4)){ //zj sj String[] e = a.split("-"); String[] f = e[1].split(" "); String stime=f[3]+" "+f[4]; String etime=f[5]+" "+f[6]; Date starttime = new Date(); Date endtime = new Date(); SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); try { starttime = sdf3.parse(stime); } catch (ParseException e2) { e2.printStackTrace(); } try { endtime = sdf3.parse(etime); } catch (ParseException e1) { e1.printStackTrace(); } CallRecord cal = new CallRecord(starttime,endtime,f[0].substring(0, 4),f[2]); if(treeMap.get(f[0])!=null&&treeMap.size()!=0){ if(f[2].equals("0791")) { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInCityRecords(cal);} } else if(f[2].matches(q)) { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInProvinceRecords(cal);} } else { if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addCallingInLandRecords(cal); } if(treeMap.get(f[1])!=null){ treeMap.get(f[1]).getUserRecords().addAnswerInLandRecords(cal); } } } }
3、题目集10
这次题目与其它题目不同之处在于要计算字符的个数,并以此为基础计算费用:
public class Main { public static void main(String[] args) { // TODO Auto-generated method stub Scanner in = new Scanner(System.in); String a = in.nextLine(); String s111 = "u-1\\d{10}\\s3"; String s21 = "m-1\\d{10}\\s1\\d{10}\\s([a-z]|[A-Z]|[0-9]|[,]|[.]|[ ]){1,}"; TreeMap<String,User> treeMap = new TreeMap<String,User>(); while(!a.equals("end")) { String[] s1 = a.split(" "); if(a.matches(s111)) { String[] e = a.split("-"); String[] f = e[1].split(" "); User user=new User(f[0],new MessageCharing()); treeMap.put(f[0],user); } if(a.matches(s21)) { String[] e = a.split("-"); String[] f = e[1].split(" "); String s11 = s1[2]; MessageRecord mes = new MessageRecord(s11); if(treeMap.get(f[0])!=null){ treeMap.get(f[0]).getUserRecords().addSendMessageRecords(mes); } } a = in.nextLine(); } for(User user:treeMap.values()) { System.out.print(user.getNumber()+" "); System.out.printf("%.1f %.1f\n",user.calCost(),user.calBalance()); } } } abstract class ChargeMode { ArrayList<ChargeRule> chargeRules = new ArrayList<>(); public ArrayList<ChargeRule> getChargeRules() { return chargeRules; } public void setChargeRules(ArrayList<ChargeRule> chargeRules) { this.chargeRules = chargeRules; } public double calCost(UserRecords userRecords) { return 0; } } abstract class ChargeRule { public abstract double calCost(ArrayList<MessageRecord> sendMessageRecords); } abstract class CommunicationRecord { protected String callingNumber; protected String answeringNumber; public String getCallingNumber() { return callingNumber; } public void setCallingNumber(String callingNumber) { this.callingNumber = callingNumber; } public String getAnsweringNumber() { return answeringNumber; } public void setAnsweringNumber(String answeringNumber) { this.answeringNumber = answeringNumber; } } abstract class MessageChargeRule extends ChargeRule{ public double calCost(UserRecords userRecords) { // TODO 自动生成的方法存根 return 0; } } class MessageCharing extends ChargeMode { public double calCost(UserRecords userRecords) { getChargeRules().add(new SendMessageRule()); double abs =0; double a = super.getChargeRules().get(0).calCost(userRecords.getSendMessageRecords()); abs = a+abs; return abs; } } class MessageRecord extends CommunicationRecord{ private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public MessageRecord(String message) { super(); this.message = message; } } class SendMessageRule extends ChargeRule{ @Override public double calCost(ArrayList<MessageRecord> sendMessageRecords) { // TODO 自动生成的方法存根 int sum = 0; for(MessageRecord b: sendMessageRecords) { sum += (int)(Math.ceil(b.getMessage().length()/10.0)); } if(sum<=3) return sum*0.1; else if(sum<=5) return 0.3+(sum-3)*0.2; else return 0.7+(sum-5)*0.3; } } class User{ UserRecords userRecords = new UserRecords(); private double balance = 100 ; ChargeMode chargemode; private String number; public User(String number, ChargeMode chargemode) { // TODO 自动生成的构造函数存根 this.chargemode=chargemode; this.number=number; } public double getBalance() { return balance ; } public UserRecords getUserRecords() { return userRecords; } public void setUserRecords(UserRecords userRecords) { this.userRecords = userRecords; } public double calBalance() { return balance-chargemode.calCost(userRecords); } public double calCost() { return chargemode.calCost(userRecords); } public ChargeMode getChargemode() { return chargemode; } public void setChargemode(ChargeMode chargemode) { this.chargemode = chargemode; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } } class UserRecords{ ArrayList<MessageRecord> sendMessageRecords = new ArrayList<MessageRecord>(); ArrayList<MessageRecord> receiveMessageRecords= new ArrayList<MessageRecord>(); public ArrayList<MessageRecord> getSendMessageRecords() { return sendMessageRecords; } public void setSendMessageRecords(ArrayList<MessageRecord> sendMessageRecords) { sendMessageRecords = sendMessageRecords; } public ArrayList<MessageRecord> getReceiveMessageRecords() { return receiveMessageRecords; } public void setReceiveMessageRecords(ArrayList<MessageRecord> receiveMessageRecords) { this.receiveMessageRecords = receiveMessageRecords; } public void addSendMessageRecords(MessageRecord sendMessageRecord) { sendMessageRecords.add(sendMessageRecord); } public void addReceiveMessageRecords(MessageRecord receiveMessageRecord) { receiveMessageRecords.add(receiveMessageRecord); } }
三、踩坑心得
就拿题目集10的后两个题目来说:要注意内部类与外部类之间的联系,特别是内部类方法的应用要注意。
public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int n = input.nextInt(); Shop myshop = new Shop(n); Shop.InnerCoupons in = myshop.new InnerCoupons(50); in.setValue(50); in.buy(); in.setValue(100); in.buy(); } } class Shop { private int milkCount; InnerCoupons coupons50; InnerCoupons coupons100; public Shop(int milkCount) { super(); this.milkCount = milkCount; } public int getMilkCount() { return milkCount; } public void setMilkCount(int milkCount) { this.milkCount = milkCount; } class InnerCoupons{ public int value; public InnerCoupons(int value) { super(); this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public void buy() { System.out.println("使用了面值为"+value+"的购物券进行支付"); int t = Shop.this.getMilkCount(); t = Shop.this.getMilkCount()-value/50; Shop.this.setMilkCount(t); System.out.println("牛奶还剩"+t+"箱"); } } }
这个即是抽象类的联系
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) { System.out.println(animal.getAnimalClass()+"的叫声:"+animal.shout()); } } //定义抽象类Animal abstract class Animal{ public String getAnimalClass() { return ""; } public String shout() { return ""; } } //基于Animal类,定义猫类Cat,并重写两个抽象方法 class Cat extends Animal{ public String getAnimalClass() { return "猫"; } public String shout() { return "喵喵"; } } //基于Animal类,定义狗类Dog,并重写两个抽象方法 class Dog extends Animal{ public String getAnimalClass() { return "狗"; } public String shout() { return "汪汪"; } } //基于Animal类,定义山羊类Goat,并重写两个抽象方法 class Goat extends Animal{ public String getAnimalClass() { return "山羊"; } public String shout() { return "咩咩"; } }
四、改进建议
我发现在题目08的地方可以把输出改成更加方便的输出:
for(User user:treeMap.values()) { System.out.println("0791"+user.getNumber()+" "+user.calCost()+" "+user.calBalance()); }
改为:
for(User user:treeMap.values()) { System.out.println(user.getNumber()+" "+user.calCost()+" "+user.calBalance()); }
这是我发现可以改进的地方。
五、总结
在这个12-15周之间我经历了很多困难和失望,无论是对自己还是这个我待的地方,究竟是哪个地方出现了错误,不过现在这些都不用太过在意,我已经理解了我自己的能力究竟能做到多少,不是因为其它人就是我自己没有学好这门课,我想说的就这么多。