JAVA学习之PTA题目集4~6的总结
一、前言:
总结三次题目集的知识点、题量、难度等情况
题目集四题量不多,其中 7-1 水文数据校验及处理 对正则表达式的书写以及运用有较高要求,7-2,7-3则是对继承,封装等内容的练习。前一题难度较高,后两题难度不大。
题目集五题目量中等,第一题是对继承封装聚合设计的另一种设计方式,主要需要掌封装和继承的使用,难度和题目集四7-2差别不大。7-4中检测Java关键字的题目难度较高,也是需要对正则表达式的熟练运用。题目集五中7-2,7-3则是对算法的一些基本联系,对于冒泡排序,选择排序,插入排序进行复习。
题目集六整体难度一般,题目数量较多,由于前面两次题目集对正则表达式的联系较多,本次题目集中7-1,7-3,7-4中的正则表达式练习是基础练习,题目集六中7-2是一道排序算法,7-5需要掌握封装,继承和多态,难度适中。7-6则需要使用接口来进行对不同的图形有不同的计算方法,需要学习知识较多,难度适中。
二、设计与分析:
(1)两种日期类的聚合设计
题目:
设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1820,2020] ,month∈[1,12] ,day∈[1,31]
应用程序共测试三个功能:
- 求下n天
- 求前n天
- 求两个日期相差的天数
注意:严禁使用Java中提供的任何与日期相关的类与方法,并提交完整源码,包括主类及方法(已提供,不需修改)
输入格式:
有三种输入方式(以输入的第一个数字划分[1,3]):
- 1 year month day n //测试输入日期的下n天
- 2 year month day n //测试输入日期的前n天
- 3 year1 month1 day1 year2 month2 day2 //测试两个日期之间相差的天数
输出格式:
- 当输入有误时,输出格式如下:
Wrong Format
- 当第一个数字为1且输入均有效,输出格式如下:
year1-month1-day1 next n days is:year2-month2-day2
- 当第一个数字为2且输入均有效,输出格式如下:
year1-month1-day1 previous n days is:year2-month2-day2
- 当第一个数字为3且输入均有效,输出格式如下:
The days between year1-month1-day1 and year2-month2-day2 are:值
输入样例1:
在这里给出一组输入。例如:
3 2014 2 14 2020 6 14
输出样例1:
在这里给出相应的输出。例如:
The days between 2014-2-14 and 2020-6-14 are:2312
输入样例2:
在这里给出一组输入。例如:
2 1834 2 17 7821
输出样例2:
在这里给出相应的输出。例如:
1834-2-17 previous 7821 days is:1812-9-19
输入样例3:
在这里给出一组输入。例如:
1 1999 3 28 6543
输出样例3:
在这里给出相应的输出。例如:
1999-3-28 next 6543 days is:2017-2-24
输入样例4:
在这里给出一组输入。例如:
0 2000 5 12 30
输出样例4:
在这里给出相应的输出。例如:
Wrong Format
第一种日期聚合设计:
题目集4(7-2)
源代码如下:
import java.util.*; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int type=0; int inputyear,inputmonth, inputday, inputn; int inputyear2,inputmonth2, inputday2; String typestr; type=input.nextInt(); if(type==1) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputn=input.nextInt(); input.close(); if(Date.checkInputValidity()==false) System.out.println("Wrong Format"); else { Date.getNextNDays(inputn); System.out.println(Date.showdate()); } } else if(type==2) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputn=input.nextInt(); input.close(); if(Date.checkInputValidity()==false) System.out.println("Wrong Format"); else { Date.getPreviousNDays(inputn); System.out.println(Date.showdate()); } } else if(type==3) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputyear2=input.nextInt(); inputmonth2=input.nextInt(); inputday2=input.nextInt(); DateUtil Date2=new DateUtil(inputyear2,inputmonth2,inputday2); input.close(); if(Date.checkInputValidity()==false||Date2.checkInputValidity()==false) System.out.println("Wrong Format"); else System.out.println(Date.getDaysofDates(Date2)); } } } class DateUtil{ DateUtil() { } DateUtil(int y,int m, int d) { this.day=new Day(y,m,d); } Day day=new Day(); Day getday() { return day; } void setday(Day d) { day.value=d.value; } boolean checkInputValidity() { if(day.month.year.validate()==false) return false; else if(day.month.validate()==false) return false; else if(day.validate()==false) return false; return true; } boolean compareDates(DateUtil date) { if(day.month.year.value<date.day.month.year.value) return true; else { if(day.month.year.value>date.day.month.year.value) return false; else { if(day.month.value<date.day.month.value) return true; else { if(day.month.value>date.day.month.value) return false; else { if(day.value<date.day.value) return true; else return false; } } } } } boolean equalTwoDates(DateUtil date) { if(day.month.year.value==date.day.month.year.value&&day.month.value==date.day.month.value&&day.value==date.day.value) return true; else return false; } String showdate() { return day.month.year.value+"-"+day.month.value+"-"+day.value; } DateUtil getNextNDays(int n) { int i; for(i=0;i<n;i++) { day.dayIncrement(); } return this; } DateUtil getPreviousNDays(int n) { int i; for(i=0;i<n;i++) { day.dayReduction(); } return this; } int getDaysofDates(DateUtil date) { int sum=0; if(compareDates(date)==false) { while(equalTwoDates(date)!=true) { date.day.dayIncrement(); sum++; } } else { while(equalTwoDates(date)!=true) { this.day.dayIncrement(); sum++; } } return sum; } } class Year{ int value; Year(){ } Year(int year){ this.value=year; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } boolean isLeapYear() { if(value%400==0) return true; else if(value%4==0&&value%100!=0) return true; else return false; } boolean validate() { if(value>=1900&&value<=2050) return true; else return false; } void yearIncrement() { value++; } void yearReduction() { value--; } } class Month{ Month(){ } Month(int yearvalue,int monthvalue) { this.value=monthvalue; this.year=new Year(yearvalue); } int value; Year year=new Year(); public int getValue() { return value; } public void setValue(int value) { this.value = value; } Year getyear() { return year; } void setyear(Year value) { year.setValue(value.value); } void resetmin() { value=1; } void resetmax() { value=12; } boolean validate() { if(value>=1&&value<=12) return true; else return false; } void monthIncrement() { if(value==12) { resetmin(); year.yearIncrement(); } else value++; } void monthReduction() { if(value==1) { resetmax(); year.yearReduction(); } else value--; } } class Day{ int value; Month month=new Month(); int []mon_maxnum= {0,31,28,31,30,31,30,31,31,30,31,30,31}; Day() { } Day(int yearvalue,int monthvalue,int dayvalue){ this.value=dayvalue; this.month=new Month(yearvalue,monthvalue); } public int getValue() { return value; } public void setValue(int value) { this.value = value; } Month getMonth() { return month; } void setMonth(Month value) { month.setValue(value.value); } void resetMin() { value=1; } void resetMax() { value=mon_maxnum[month.value]; } boolean validate() { if(value>31||value<1) return false; if(month.value==2) { if(value>28&&month.year.isLeapYear()==false) return false; if(value>29&&month.year.isLeapYear()==true) return false; } else if(month.value==4||month.value==6||month.value==9||month.value==11) { if(value>30) return false; } return true; } void dayIncrement() { if(month.value==2&&month.year.isLeapYear()==true) mon_maxnum[2]=29; else if(month.value==2&&month.year.isLeapYear()==false) mon_maxnum[2]=28; if(value==mon_maxnum[month.value]){ month.monthIncrement(); resetMin(); } else value++; } void dayReduction() { if(month.value==3&&month.year.isLeapYear()==true) mon_maxnum[2]=29; else if(month.value==3&&month.year.isLeapYear()==false) mon_maxnum[2]=28; if(value==1){ month.monthReduction(); resetMax(); } else value--; } }
第二种日期设计:
题目集5(7-4)
源代码如下:
import java.util.*; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int type=0; int inputyear,inputmonth, inputday, inputn; int inputyear2,inputmonth2, inputday2; String typestr; type=input.nextInt(); if(type==1) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputn=input.nextInt(); input.close(); if(Date.checkInputValidity()==false) System.out.println("Wrong Format"); else { System.out.print(Date.showdate()); Date.getNextNDays(inputn); System.out.println(" next "+inputn+" days is:"+Date.showdate()); } } else if(type==2) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputn=input.nextInt(); input.close(); if(Date.checkInputValidity()==false) System.out.println("Wrong Format"); else { System.out.print(Date.showdate()); Date.getPreviousNDays(inputn); System.out.print(" previous "+inputn+" days is:"+Date.showdate()); } } else if(type==3) { inputyear=input.nextInt(); inputmonth=input.nextInt(); inputday=input.nextInt(); DateUtil Date=new DateUtil(inputyear,inputmonth,inputday); inputyear2=input.nextInt(); inputmonth2=input.nextInt(); inputday2=input.nextInt(); DateUtil Date2=new DateUtil(inputyear2,inputmonth2,inputday2); input.close(); if(Date.checkInputValidity()==false||Date2.checkInputValidity()==false) System.out.println("Wrong Format"); else if(Date.compareDates(Date2)!=false) System.out.println("The days between "+Date.showdate()+" and "+Date2.showdate()+" are:"+Date.getDaysofDates(Date2)); else System.out.println("The days between "+Date.showdate()+" and "+Date2.showdate()+" are:"+Date2.getDaysofDates(Date)); } else System.out.println("Wrong Format"); } } class DateUtil{ Year y=new Year(); Month m=new Month(); Day d=new Day(); int []mon_max= {0,31,28,31,30,31,30,31,31,30,31,30,31}; DateUtil() { } DateUtil(int y,int m, int d) { this.d.setValue(d); this.m.setValue(m); this.y.setValue(y); } public Year getYear() { return y; } public void setYear(Year y) { this.y = y; } public Month getMon() { return m; } public void setMon(Month m) { this.m = m; } public Day getDay() { return d; } public void setDay(Day d) { this.d = d; } public void setDaymin() { d.setValue(1); } public void setDaymax() { d.setValue(mon_max[m.getValue()]); } boolean checkInputValidity() { if(y.validate()==false) return false; if(m.validate()==false) return false; if(y.isLeapYear()==false) mon_max[2]=28; else mon_max[2]=29; if(d.getValue()>mon_max[m.getValue()]) return false; return true; } boolean compareDates(DateUtil date) { if(y.getValue()<date.y.getValue()) return true; else { if(y.getValue()>date.y.getValue()) return false; else { if(m.getValue()<date.m.getValue()) return true; else { if(m.getValue()>date.m.getValue()) return false; else { if(d.getValue()<date.d.getValue()) return true; else return false; } } } } } boolean equalTwoDates(DateUtil date) { if(y.getValue()==date.y.getValue()&&m.getValue()==date.m.getValue()&&d.getValue()==date.d.getValue()) return true; else return false; } public DateUtil getNextNDays(int n) { int i; for(i=0;i<n;i++) { if(d.getValue()==mon_max[m.getValue()]) { m.monthIncrement(y); if(m.getValue()==2) { if(y.isLeapYear()==true) mon_max[2]=29; else mon_max[2]=28; } d.setValue(1); } else d.dayIncrement(); } return this; } public DateUtil getPreviousNDays(int n) { int i; for(i=0;i<n;i++) { if(d.getValue()==1) { m.monthReduction(y); if(m.getValue()==2) { if(y.isLeapYear()==true) mon_max[2]=29; else mon_max[2]=28; } d.setValue(mon_max[m.getValue()]); } else d.dayReduction(); } return this; } public int getDaysofDates(DateUtil date) { int sum=0; while(equalTwoDates(date)!=true) { if(d.getValue()==mon_max[m.getValue()]) { m.monthIncrement(y); if(m.getValue()==2) { if(y.isLeapYear()==true) mon_max[2]=29; else mon_max[2]=28; } d.setValue(1); } else d.dayIncrement(); sum++; } return sum; } public String showdate() { return y.getValue()+"-"+m.getValue()+"-"+d.getValue(); } } class Year{ private int value; Year(){ } Year(int year){ this.value=year; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } boolean isLeapYear() { if(value%400==0) return true; else if(value%4==0&&value%100!=0) return true; else return false; } boolean validate() { if(value>=1820&&value<=2020) return true; else return false; } void yearIncrement() { value++; } void yearReduction() { value--; } } class Month{ Month(){ } Month(int monthvalue) { this.value=monthvalue; } private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } void resetmin() { value=1; } void resetmax() { value=12; } boolean validate() { if(value>=1&&value<=12) return true; else return false; } void monthIncrement(Year year) { if(value==12) { resetmin(); year.yearIncrement(); } else value++; } void monthReduction(Year year) { if(value==1) { resetmax(); year.yearReduction(); } else value--; } } class Day{ private int value; Day() { } Day(int dayvalue){ this.value=dayvalue; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } void dayIncrement() { value++; } void dayReduction() { value--; } }
两种日期类聚合设计的优劣比较:
SMsut-up分析两种写法:
第一类设计:
第二类设计:
可以看出在工具下判断两者并没有太大的差别,无论你最大复杂性还是最大深度,还是平均深度等参数,两者的Kiviat图的分布和值时差不太多的。
个人评测:
第一类设计在类中对另一个设计(年在月,月在日)中,对于一些函数的编写会显得十分轻松,讨论时也非常简单,例如对于增加月份的操作,第一类设计可以在12月份增加到1月后调用Year里面的增加年份的函数,使得完成整个增加月份的后续所有操作,到Days函数中最为明显,日期增加可以直接调用Month的Month_increase函数对月份增加,而月份增加函数中包含了对年份的增加,使得讨论时不需要关注年份数值的需要变化。使得在一个功能实现时不需要分析对于其底层的变动。
缺陷:分析代码时可读性差,对于他人阅读代码时,需要对函数层层深入才能了解函数的逻辑。其中,当程序出现问题时,问题藏在多层中的其中一层,检查起来比较麻烦,若需要对需求进行更改,需要从底层代码进行改动,层层递进都需要更改,修改和维护工作量大。
第二类设计分开不同类进行设计和调用,对于bug检查和修改比较直观。函数之间联系较小,全复杂度低,需要改动需求的话,不需要对不同类的修改过多,利于他人阅读分析。
缺陷:讨论问题时,需要使用不同的类,对于类中的属性值的更改较多,考虑的内容较多,例如日期增加就需要考虑月份是否增加,月份增加后会不会对年份增加等等,逻辑较为清晰,但书写复杂。
(2)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等)
题目集4(7-3)
编写程序,实现图形类的继承,并定义相应类对象并进行测试。
- 类Shape,无属性,有一个返回0.0的求图形面积的公有方法
public double getArea();//求图形面积
- 类Circle,继承自Shape,有一个私有实型的属性radius(半径),重写父类继承来的求面积方法,求圆的面积
- 类Rectangle,继承自Shape,有两个私有实型属性width和length,重写父类继承来的求面积方法,求矩形的面积
- 类Ball,继承自Circle,其属性从父类继承,重写父类求面积方法,求球表面积,此外,定义一求球体积的方法
public double getVolume();//求球体积
- 类Box,继承自Rectangle,除从父类继承的属性外,再定义一个属性height,重写父类继承来的求面积方法,求立方体表面积,此外,定义一求立方体体积的方法
public double getVolume();//求立方体体积
- 注意:
- 每个类均有构造方法,且构造方法内必须输出如下内容:
Constructing 类名
- 每个类属性均为私有,且必须有getter和setter方法(可用Eclipse自动生成)
- 输出的数值均保留两位小数
主方法内,主要实现四个功能(1-4): 从键盘输入1,则定义圆类,从键盘输入圆的半径后,主要输出圆的面积; 从键盘输入2,则定义矩形类,从键盘输入矩形的宽和长后,主要输出矩形的面积; 从键盘输入3,则定义球类,从键盘输入球的半径后,主要输出球的表面积和体积; 从键盘输入4,则定义立方体类,从键盘输入立方体的宽、长和高度后,主要输出立方体的表面积和体积;
假如数据输入非法(包括圆、矩形、球及立方体对象的属性不大于0和输入选择值非1-4),系统输出Wrong Format
输入格式:
共四种合法输入
- 1 圆半径
- 2 矩形宽、长
- 3 球半径
- 4 立方体宽、长、高
输出格式:
按照以上需求提示依次输出
输入样例1:
在这里给出一组输入。例如:
1 1.0
输出样例1:
在这里给出相应的输出。例如:
Constructing Shape
Constructing Circle
Circle's area:3.14
输入样例2:
在这里给出一组输入。例如:
4 3.6 2.1 0.01211
输出样例2:
在这里给出相应的输出。例如:
Constructing Shape
Constructing Rectangle
Constructing Box
Box's surface area:15.26
Box's volume:0.09
输入样例3:
在这里给出一组输入。例如:
2 -2.3 5.110
输出样例2:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.util.*; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int n=input.nextInt(); if(n==1) { double radius=input.nextDouble(); if(radius>0) { Circle c=new Circle(); c.setRadius(radius); System.out.printf("Circle's area:%.2f",c.getArea()); } else System.out.println("Wrong Format"); } else if(n==2) { double w,l; w=input.nextDouble(); l=input.nextDouble(); if(w>0&&l>0) { Rectangle re=new Rectangle(); re.setLength(l); re.setWidth(w); System.out.printf("Rectangle's area:%.2f\n",re.getArea()); } else System.out.println("Wrong Format"); } else if(n==3) { double radius=input.nextDouble(); if(radius>0) { Ball ball=new Ball(); ball.setRadius(radius); System.out.printf("Ball's surface area:%.2f\n",ball.getArea()); System.out.printf("Ball's volume:%.2f",ball.getVolume()); } else System.out.println("Wrong Format"); } else if(n==4) { double w,l,h; w=input.nextDouble(); l=input.nextDouble(); h=input.nextDouble(); if(w>0&&l>0&&h>0) { Box box=new Box(); box.setLength(l); box.setWidth(w); box.setHight(h); System.out.printf("Box's surface area:%.2f\n",box.getArea()); System.out.printf("Box's volume:%.2f",box.getVolume()); } else System.out.println("Wrong Format"); } else System.out.println("Wrong Format"); } } class Shape { Shape(){ System.out.println("Constructing Shape"); } public double getArea() { return 0.0; } } class Circle extends Shape { Circle() { System.out.println("Constructing Circle"); } private double radius; public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public double getArea() { return Math.PI*this.radius*this.radius; } } class Rectangle extends Shape{ private double width,length; Rectangle() { System.out.println("Constructing Rectangle"); } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double getArea() { return this.length*this.width; } } class Ball extends Circle{ Ball() { System.out.println("Constructing Ball"); } public double getArea() { return 4.0*super.getArea(); } public double getVolume() { double r=super.getRadius(); double volume; volume=4.0/3.0*r*super.getArea(); return volume; } } class Box extends Rectangle{ private double hight; Box() { System.out.println("Constructing Box"); } public double getHight() { return hight; } public void setHight(double hight) { this.hight = hight; } public double getArea() { double l,w,area; l=super.getLength(); w=super.getWidth(); area=2*(l*w+hight*l+hight*w); return area; } public double getVolume() { return super.getLength()*super.getWidth()*hight; } }
题目集6(7-5)
输入格式:
从键盘首先输入三个整型值(例如a b c),分别代表想要创建的Circle、Rectangle及Triangle对象的数量,然后根据图形数量继续输入各对象的属性值(均为实型数),数与数之间可以用一个或多个空格或回车分隔。
输出格式:
- 如果图形数量非法(小于0)或图形属性值非法(数值小于0以及三角形三边关系),则输出
Wrong Format
。 - 如果输入合法,则正常输出,输出内容如下(输出格式见输入输出示例):
- 各个图形的面积;
- 所有图形的面积总和;
- 排序后的各个图形面积;
- 再次所有图形的面积总和。
输入样例1:
在这里给出一组输入。例如:
1 1 1 2.3 3.2 3.2 6.5 3.2 4.2
输出样例1:
在这里给出相应的输出。例如:
Original area: 16.62 10.24 5.68 Sum of area:32.54 Sorted area: 5.68 10.24 16.62 Sum of area:32.54
输入样例2:
在这里给出一组输入。例如:
0 2 2 2.3 2.5 56.4 86.5 64.3 85.6 74.6544 3.2 6.1 4.5
输出样例2:
在这里给出相应的输出。例如:
Original area: 5.75 4878.60 2325.19 7.00 Sum of area:7216.54 Sorted area: 5.75 7.00 2325.19 4878.60 Sum of area:7216.54
输入样例3:
在这里给出一组输入。例如:
0 0 1 3 3 6
输出样例3:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.util.ArrayList; import java.util.Scanner; public class Main { public static void main(String[] args) { int circlenum,recnum,trinum,i,flag=0; double raduis,length,width,a,b,c; ArrayList<Shape> list = new ArrayList<>(); Scanner input= new Scanner(System.in); circlenum=input.nextInt(); recnum=input.nextInt(); trinum=input.nextInt(); if(circlenum<0||recnum<0||trinum<0) { System.out.println("Wrong Format"); System.exit(0); } for(i=0;i<circlenum;i++) { raduis=input.nextDouble(); Shape circle=new Circle(raduis); if(circle.validate()) list.add(circle); else flag=1; } for(i=0;i<recnum;i++) { length=input.nextDouble(); width=input.nextDouble(); Shape t=new Rectangle(length,width); if(t.validate()) list.add(t); else flag=1; } for(i=0;i<trinum;i++) { a=input.nextDouble(); b=input.nextDouble(); c=input.nextDouble(); Shape tri = new Triangle(a,b,c); if(tri.validate()) list.add(tri); else flag=1; } if(flag==0) { double []arry=new double[list.size()]; for(i=0;i<list.size();i++) { arry[i]=list.get(i).getArea(); } System.out.println("Original area:"); show(arry); sum(arry); sort(arry); System.out.println("Sorted area:"); show(arry); sum(arry); } else { System.out.println("Wrong Format"); } } public static void sum(double []arry) { int i; double sum=0; for(i=0;i<arry.length;i++) { sum=sum+arry[i]; } System.out.printf("Sum of area:%.2f\n",sum); } public static void sort(double []arry) { int i,j; double t; int now; for(i=0;i<arry.length;i++) { now=i; for(j=i;j>=0;j--) { if(arry[now]<arry[j]) { t=arry[now]; arry[now]=arry[j]; arry[j]=t; now=j; } } } } public static void show(double []arry) { int i; for(i=0;i<arry.length;i++) System.out.printf("%.2f ",arry[i]); System.out.println(); } } abstract class Shape extends Object{ public abstract double getArea(); public abstract boolean validate(); public String toString; } class Circle extends Shape{ private double raduis; Circle() { } Circle(double r) { this.raduis=r; } public double getArea() { return Math.PI*raduis*raduis; } public boolean validate() { if(this.raduis<=0) return false; else return true; } } class Rectangle extends Shape{ double width; double length; Rectangle() { } Rectangle(double l,double w) { this.length=l; this.width=w; } public double getArea() { return length*width; } public boolean validate() { if(length<=0||width<=0) return false; else return true; } } class Triangle extends Shape{ double side1,side2,side3; Triangle() { } Triangle(double s1,double s2,double s3) { this.side1=s1; this.side2=s2; this.side3=s3; } public double getArea() { double p=(side1+side2+side3)/2; double area=Math.sqrt(p*(p-side1)*(p-side2)*(p-side3)); return area; } public boolean validate() { if(side1+side2>side3&&side1+side3>side2&&side2+side3>side1) return true; else return false; } }
题目集6(7-6)
编写程序,使用接口及类实现多态性,类图结构如下所示:
其中:
- GetArea为一个接口,无属性,只有一个GetArea(求面积)的抽象方法;
- Circle及Rectangle分别为圆类及矩形类,分别实现GetArea接口
- 要求:在Main类的主方法中分别定义一个圆类对象及矩形类对象(其属性值由键盘输入),使用接口的引用分别调用圆类对象及矩形类对象的求面积的方法,直接输出两个图形的面积值。(要求只保留两位小数)
输入格式:
从键盘分别输入圆的半径值及矩形的宽、长的值,用空格分开。
输出格式:
- 如果输入的圆的半径值及矩形的宽、长的值非法(≤0),则输出
Wrong Format
- 如果输入合法,则分别输出圆的面积和矩形的面积值(各占一行),保留两位小数。
输入样例1:
在这里给出一组输入。例如:
2 3.6 2.45
输出样例1:
在这里给出相应的输出。例如:
12.57 8.82
输入样例2:
在这里给出一组输入。例如:
9 0.5 -7.03
输出样例2:
在这里给出相应的输出。例如:
Wrong Format
源代码:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); double radius = input.nextDouble(); double length = input.nextDouble(); double width = input.nextDouble(); if(radius<=0||length<=0||width<=0) System.out.println("Wrong Format"); else { Circle c=new Circle(radius); Rectangle r=new Rectangle(length,width); System.out.printf("%.2f\n",c.getArea()); System.out.printf("%.2f",r.getArea()); } } } interface GetArea{ public double getArea(); } class Circle implements GetArea{ private double radius; public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } Circle() { } Circle(double r) { this.radius=r; } public double getArea() { return Math.PI*radius*radius; } } class Rectangle implements GetArea{ private double width; private double length; public double getWidth() { return width; } Rectangle() { } Rectangle(double l,double w) { this.length=l; this.width=w; } public void setWidth(double width) { this.width = width; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double getArea() { return length*width; } }
分析:
对Java中三种渐进式图形继承设计,题目集4(7-3)中采取了封装和继承,对于一些具有公共特征而细节和实现方式不一样的类可以使用继承的方式来进行代码复用,例如图形类中圆,矩形,球等有不同的求面积方式,相较于平面图形,立体图行还有体积一说,可以对父类进行扩展实现子类之间的多样性。
题目集6(7-5)中就有抽象父类shape,抽象类是无法拥有实体的,但是其继承的时候子类和父类存在一些会影响构建类时的差距,例如父类为矩形,子类为正方形,从正方形是特殊的长方形这一点来说,是可以这样来构造关系的,但是,如果需求中需要对长宽进行修改,但是如果子类继承该方法时,就会对从父类继承到的属性中更改,因此,正方形也会在拉伸时变成长方形。但如果使用抽象父类图形类,矩形和正方形同时继承抽象父类Shape,然后不同图形有自己不同的修改方式,这样正方形和长方形变不会冲突。
题目集6(7-6)使用接口来完成相关功能,会使得代码复用率更高,其中同一个方法可以针对不同的类别来进行自己的操作,例如sort函数后面接组数字,就进行从小到大的排序,如果是字母就按照首字母的顺序排序,如果是颜色可以对自定义颜色先后排序。其中实现过程就是用compare接口,其中Array 中的Sort就调用了该接口。对不同的比较只需要修改重写compareTo函数即可,这样可以用最小的改动量来实现新的功能。
(3)正则表达式
三次题目集中或多或少都涉及到正则表达式的书写,其中题目集四点7-1,题目集五的7-4,题目集六的7-1,7-3,7-4都是对正则表达式的运用。正则表达式运用场景主要在String中进行字符或字符串匹配,例如String x.matches("正则表达式");在冒号中间书写正则表达式,或者replace函数中对正则表达式进行关键词替换,splite对字符串匹配的点进行拆分,还有equal函数对字符串进行精准匹配。
以下对正则表达式进行一些整理:
[ ]代表一个字符匹配,
{ }中表达该字符需要匹配的个数,
.*表示任意多个字符匹配
话不多说,直接亮代码:
这是题目集五的7-4部分代码
class processString { public String dropNoteDoubleLine(String x) { String y=x.replaceAll("[/][/](.*)"," "); return y; } public String dropNote(String x) { String y=x.replaceAll("[/][\\*](.*?)[\\*][/]", " "); return y; } public String dropString(String x) { String y=x.replaceAll("[\\u0022][^(\\u0022)]*[\\u0022]"," "); return y; } public String dropSpecialChar(String x) { String y=x.replaceAll("[\\W]"," "); return y; }
}
dropNoteDoubleLine是对//后面所有的内容(包括//)进行除去,去除方式为将其内容转化为一个空格。
dropNote是对/*xxxx*/中的内容进行去除,(.*?)是匹配任意长度的任意字符,但?的目的是当匹配条件满足时最短的目标内容进行返回,这题测试点中就存以下模式:
eg: /*drop this line */ this.note="hello" /*give value to this note*/
本身只需要去除两边注释内的消息得到 this.note="hello" ,而不加?进行设置则会把最长匹配条件返回,他会将字符串解读成
/*drop this line */ this.note="hello" /*give value to this note*/,将其全部删除,从而使得返回结果不正确。
public String dropString(String x)是对""中的内容进行删除,匹配条件为第一个是",下一个为任意字符,最后一个为"。但使用?会更方便。
public String dropSpecialChar(String x)是去掉特殊字符,\\W是对非命名条件(非数字,非字母,非下划线等)的字符进行匹配,从而去除特殊符号。
以上便是我个人对于正则表达式的理解。
(4)题目集5(7-4)中Java集合框架应用的分析总结
编写程序统计一个输入的Java源码中关键字(区分大小写)出现的次数。说明如下:
- Java中共有53个关键字(自行百度)
- 从键盘输入一段源码,统计这段源码中出现的关键字的数量
- 注释中出现的关键字不用统计
- 字符串中出现的关键字不用统计
- 统计出的关键字及数量按照关键字升序进行排序输出
- 未输入源码则认为输入非法
输入格式:
输入Java源码字符串,可以一行或多行,以exit
行作为结束标志
输出格式:
- 当未输入源码时,程序输出
Wrong Format
- 当没有统计数据时,输出为空
- 当有统计数据时,关键字按照升序排列,每行输出一个关键字及数量,格式为
数量\t关键字
先展示类图:
圈复杂度分析:
使用StringBuilder对大量字符串进行处理后的存储,再进行整合后处理,分组最后在进行不同字符串的精准定位和判断。
对于很多测试情况的处理,是非常头疼,对很多特殊情况测试后最后提交结果:
源码如下:
public class Main { public static void main(String[] args) { java.util.Scanner input = new java.util.Scanner(System.in); int size = input.nextInt(); int[] list = new int[size]; for (int i = 0; i < list.length; i++) { list[i] = input.nextInt(); } int choice = input.nextInt(); switch(choice){ case 1:insertionSort(list);break; case 2:selectionSort(list);break; case 3:bubbleSort(list);break; default: break; } } public static void insertionSort(int []list) { int i,j,t; int now; for(i=0;i<list.length;i++) { now=i; for(j=i;j>=0;j--) { if(list[now]<list[j]) { t=list[now]; list[now]=list[j]; list[j]=t; now=j; } } } for(i=0;i<list.length;i++) System.out.print(list[i]+" "); } public static void selectionSort(int []list) { int i,j; int min; int t; for(i=0;i<list.length;i++) { min=i; for(j=i;j<list.length;j++) { if(list[j]<list[min]) { min=j; } } if(i!=min) { t=list[i]; list[i]=list[min]; list[min]=t; } } for(i=0;i<list.length;i++) System.out.print(list[i]+" "); } public static void bubbleSort(int []list) { int i,j; int t; for(i=0;i<list.length;i++) { for(j=0;j<list.length-1;j++) { if(list[j]>list[j+1]) { t=list[j]; list[j]=list[j+1]; list[j+1]=t; } } } for(i=0;i<list.length;i++) System.out.print(list[i]+" "); } }
三、采坑心得:
对于特殊情况判断需要花较长时间考虑,体现在题目集四中的7-1和题目集五的7-4中
截图如下:
对于封装,继承和多态的练习中,什么时候使用继承,什么时候使用接口,什么时候用抽象类,就需要一定的经验。对于一些父类不参与处理过程,只提供一些相同的属性和方法时可以使用抽象类。例如图形继承中的图形类,对于一些函数使用方法一致但对象不同的情况时,就可以采用接口来改写不同的处理方式,流程可以不更改。
使用继承:
使用继承多态:
使用接口,多态:
由于接口的题目要求条件与多态继承不一样,就不做对比了,单明显能够看到的是,使用继承多态的圈复杂明显比单继承的少很多。
四、改进建议:
对比前三次题目集(题目集一,二,三)来说,对封装性写法较为熟练了,但是对于代码可以多用继承,多态来进行代码优化,可以看到还是存在部分代码圈复杂度较高,需要进行优化。其中,对于抽象类的使用比较匮乏,对于一些父子关系的划分,调用接口还是使用抽象类,后期练习的时候可以多多增加一下该类的使用。
五、总结:
本次小结针对题目集四,五,六进行分析,在Java中,由于面对对象,常常需要对一些类的编写时会进行很多重复的操作,其中继承就帮助解决了重复代码冗余量大这么一个问题,但随着继承的使用,我们不是仅仅需要继承较少工作量,更多的体现类与类之间的不同性。继承中确实提供了方法重写,重载,但是面对一些在分类是只是有联系,但是具体划分父子关系时比较矛盾的类时,使用抽象类,接口来实现多态就会好很多,而且会存在着一个类拥有多个相似的联系或者关系,比如老虎可以是动物,有动物的特征,也是是食肉类的,有吃肉这个方法,也属于猫科动物,有着猫科动物的习性。没办法同一个单纯的父子关系将其完全描述。但是接口可以接多个,使用对应接口,对接对应的方法就可以满足这个身份,从而拥有这些方法来完成该身份下的职责,从而描述该类就更具体,更形象。但并不是所有的内容用接口,用继承就可以完成,在食堂这个课后作业就可以看出,食物大类,当时有些同学设计时,将面食,糕点,饭继承食物这个父类,但是,每个小类都是食物类有的属性和方法,也就是不具备多样性,也就是说有很多功能相似的类用同一个父类去将他们统一在一起,而不实现他们之间的不同,那还不如将其作为属性值进行区分,免得代码复杂难读。因此使用封装,继承和多态是有利于我们在编写代码时减少工作量的,但是如何更好的利用却是需要大量时间去练习,去理解的。对于实验来说,如果能够通过图形化界面来显示结果和动画,就会使得更具体和方便。本次小结就到这,希望下次学习能够熟练继承,封装和多态。