总结性Blog2
面向对象程序设计PTA02总结
一、前言
几次PTA的作业着重练习类的构造方法、方法的调用、参数传递、对象的构造与使用;练习循环结构、控制结构;练习数据的输入与输出;学习编写结构清晰、逻辑正确、功能完善的java代码。学习使用工具对代码进行跟踪调试。理解抽象类与子类的关系,实现继承操作,构造类族;正确使用抽象类和子类实现多态操作;理解接口的意义,并实现行为的多态;理解了解抽象类、类以及接口三者的异同以及使用场合。掌握UML基本用法和画法,并能根据题目描述画出类、接口、类,其中同样涉及到对正则表达式匹配与校验的使用。几次作业题中更加注重如何设计结构实现具体功能,题量虽然虽然少,但是每道题都很经典。
通过作业逐步理解面向对象的封装性、继承性与多态性三大技术特性
封装性:
在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
继承性:
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。就是通过问题之间的联系创造父类与继承父类的所有属性以及方法的子类。它可以直接用,也可以重写父类的方法,还可以定义自己特有的成分。就像图形继承题,就是通过几何图形共有的面积,周长等特征来建立的联系,也可以去特别的求出它的数据所对应的立体图形的体积。
多态性:
多态性像是父类和子类的继承关系上的一种拓展,子类一定要具有抽象父类的方法。重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。重写式多态,也叫运行时多态。这种多态通过动态绑定技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
二、设计与分析
1、对于题目集4(7-2)、题目集5(7-4)两种日期类聚合设计的优劣比较

题目集4(7-2)类图
此UML图中主要设计了Day、Dateutil、Year、Month这四个类,分别对年、月、日及日期进行操作,main类调用DateUtil类进行日期合法校验、日期是否相等、求下n天和前n天、求两个日期之间的天数,DateUtil类调用Day类进行合法性校验、日期加减,Day类调用Month类进行合法性校验、月份加减,Month类调用Year类进行合法校验,年份加减。
题目集5(7-5)
题目集5(7-5)则是由main函数调用DateUtil函数,由DateUtil函数去调用year,month,day三个函数来实现功能。两种设计方式所实现的功能相同,但是第二种实现方式更方便设计的简洁性,并且更稳定更高效,代码的整洁度更高。合成和聚合是类和类的一种关联关系。它表示一个类中拥有或包含另外一个类型,而不是就是某个类型(子类)。组合:是整体与部分的关系,整体离不开部分,部分离开整体没有意义,聚合:也是整体与部分的关系,但整体可以分离部分,部分也可以离开整体。组合/聚合:是通过获得其他对象的引用,在运行时刻动态定义的,也就是在一个对象中保存其他对象的属性,这种方式要求对象有良好定义的接口,并且这个接口不经常发生改变,而且对象只通过接口来访问,这样我们并不破坏封装性,所以只要类型一致,运行时还可通过一个对象替换另一个对象。
2.题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等 )
,

题目集6(7-6)类图
几道题主要考察封装、继承、多态、接口的实现。封装和继承是为了代码重用,然后多态是为了接口重用;封装将类里的属性和方法藏起来,使其不易被改变;继承就是子类从父类继承属性和方法,不重复编码便可以使用他们,但是子类又不仅限于父类,他还可以派生出自己特有的属性和方法;而实现多态有两种方法:重载和重写。
其中题目集4(7-3)中分别设计类Shape、类Circle继承自Shape、类Rectangle继承自Shape,类Ball,继承自Circle,类Box,继承自Rectangle,子类继承父类中的部分属性,重写父类中的方法。然后设计main类通过switch语句选择不同的数字声明不同的对象进行输出,运用了简单的封装,比较易于实现。
题目集6(7-5)此题也是对图形的面积和体积进行计算,但不同的是可创建多个拥有同一特征的多个对象需要要对多个数据进行处理,所以我使用数组来对多个数据进行处理
import java.util.Scanner; import java.util.regex.Pattern; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int a=input.nextInt(); int b=input.nextInt(); int c=input.nextInt(); Circle circle = new Circle(); Rectangle rectangle = new Rectangle(); Triangle triangle = new Triangle(); if(a<0||b<0||c<0) { System.out.println("Wrong Format"); System.exit(0); } double num1[]=new double [a]; if(a>0) { for(int i=0;i<a;i++) { num1[i]=input.nextDouble(); } for(int i=0;i<a;i++) { circle.setRadius(num1[i]); if(!circle.validate()) { System.out.println("Wrong Format"); System.exit(0); } } } double num2[][]=new double [b][2]; if(b>0) { for(int i=0;i<b;i++) { num2[i][0]=input.nextDouble(); num2[i][1]=input.nextDouble(); } for(int i=0;i<b;i++) { rectangle.setWidth(num2[i][0]); rectangle.setLength(num2[i][1]); if(!rectangle.validate()) { System.out.println("Wrong Format"); System.exit(0); } } } double num3[][]=new double [c][3]; if(c>0) { for(int i=0;i<c;i++) { num3[i][0]=input.nextDouble(); num3[i][1]=input.nextDouble(); num3[i][2]=input.nextDouble(); } for(int i=0;i<c;i++) { triangle.setSide1(num3[i][0]); triangle.setSide2(num3[i][1]); triangle.setSide3(num3[i][2]); if(!triangle.validate()) { System.out.println("Wrong Format"); System.exit(0); } } } double sumnum[]=new double[a+b+c]; if(a>0) { for(int i=0;i<a;i++) { circle.setRadius(num1[i]); sumnum[i]=circle.getArea(); } } if(b>0) { for(int i=0,j=a;i<b;i++,j++) { rectangle.setWidth(num2[i][0]); rectangle.setLength(num2[i][1]); sumnum[j]=rectangle.getArea(); } } if(c>0) { for(int i=0,j=a+b;i<c;i++,j++) { triangle.setSide1(num3[i][0]); triangle.setSide2(num3[i][1]); triangle.setSide3(num3[i][2]); sumnum[j]=triangle.getArea(); } } double sum=0; for(int i=0;i<a+b+c;i++) { sum=sum+sumnum[i]; } System.out.println("Original area:"); for(int i=0;i<a+b+c;i++) { System.out.print(String.format("%.2f", sumnum[i])+" "); } System.out.println(); System.out.println("Sum of area:"+String.format("%.2f", sum)); System.out.println("Sorted area:"); int len=a+b+c; for(int i=0;i<len-1;i++) { for(int j=0;j<len-i-1;j++) { if(sumnum[j]>sumnum[j+1]) { double temp=sumnum[j+1]; sumnum[j+1]=sumnum[j]; sumnum[j]=temp; } } } for(int i=0;i<a+b+c;i++) { System.out.print(String.format("%.2f", sumnum[i])+" "); } System.out.println(); System.out.println("Sum of area:"+String.format("%.2f", sum)); } } abstract class Shape { public abstract double getArea(); public abstract boolean validate(); public abstract String toString(); } } class Circle extends Shape{ private double radius; public Circle(){ } public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public double getArea() { return radius*radius*Math.PI; } public boolean validate() { if(this.radius>0){ return true; }else { return false; } } public String toString() { return String.format("%.2f", getArea()) + ""; } } class Rectangle extends Shape{ private double width; private double length; public Rectangle() { } public Rectangle (double width,double length) { this.width = width; this.length = length; } 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 width*length; } public boolean validate() { if(this.length>0&&this.width>0) { return true; }else { return false; } } public String toString() { return String.format("%.2f", getArea()) + ""; } } class Triangle extends Shape{ private double side1; private double side2; private double side3; public Triangle() { } public Triangle(double side1,double side2, double side3) { this.side1 = side1; this.side2 = side2; this.side3 = side3; } public double getSide1() { return side1; } public void setSide1(double side1) { this.side1 = side1; } public double getSide2() { return side2; } public void setSide2(double side2) { this.side2 = side2; } public double getSide3() { return side3; } public void setSide3(double side3) { this.side3 = side3; } public double getArea() { double p = (side1 + side2 +side3)*1/2; return Math.sqrt(p*(p-side1)*(p-side2)*(p-side3)); } public boolean validate() { if(side1>0&&side2>0&&side3>0&&side1+side2>side3&&side2+side3>side1&&side1+side3>side2) { return true; }else { return false; } } public String toString() { return String.format("%.2f", getArea()) + ""; } }
题目集6的7-5用到了继承、多态与接口。声明了父类对象Shape,用它的子类对象给它赋值。在进行比较面积并且排序时,创建一个Shape类型的泛型。通过比较类的面积进行选择排序,把元素进行排序而不单单是面积。多态性像是父类和子类的继承关系上的一种拓展,子类一定要具有抽象父类的方法。重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。重写式多态,也叫运行时多态。这种多态通过动态绑定技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
题目集6的7-5使用接口这三种渐进式图形继承设计从类图中可以看出都使用了继承,三次都具有封装性,也都利用到了多态的知识。按照题目要求来写完全可以。封装性,继承和多态是Java面向对象的三大特征,利用好这些特性可以带来很多好处。
import java.util.Scanner; interface Area { double getArea(); } class Rectangle implements Area{ private double length; private double width; public Rectangle() { } public Rectangle(double hight, double wide) { this.length = length; this.width = width; } @Override public double getArea() { return length*width; } public double getLength() { return length; } public void setLength(double length) { this.length = length; } public double getWidth() { return width; } public void setWidth(double width) { this.width = width; } public boolean validate() { if(length>0&&width>0) { return true; }else { return false; } } } class Circle implements Area{ private double radius; public Circle() { } public Circle(double radius) { this.radius = radius; } @Override public double getArea() { return radius*radius* Math.PI; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } public boolean validate() { if(this.radius>0){ return true; }else { return false; } } } public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); double r = input.nextDouble(); double w = input.nextDouble(); double l = input.nextDouble(); if(r>0&&w>0&&l>0) { Circle circle = new Circle(); circle.setRadius(r); Rectangle rectangle = new Rectangle(); rectangle.setWidth(w); rectangle.setLength(l); System.out.println(String.format("%.2f", circle.getArea())); System.out.println(String.format("%.2f", rectangle.getArea())); }else { System.out.println("Wrong Format"); } } }
3.对三次题目集中用到的正则表达式技术的分析总结
校验键盘输入的 QQ 号是否合格,判定合格的条件如下:
- 要求必须是 5-15 位;
- 0 不能开头;
- 必须都是数字;
[1-9][0-9]{4,14};
验证码是由四位数字或者字母(包含大小写)组成的字符串:
[0-9A-Za-z]{4};
对软件学院2020级同学学号进行校验,学号共八位,规则如下:
- 1、2位:入学年份后两位,例如20年
- 3、4位:学院代码,软件学院代码为20
- 5位:方向代码,例如1为软件工程,7为物联网
- 6位:班级序号
- 7、8位:学号(序号)
要求如下:
- 只针对2020级
- 其中软件工程专业班级分别为:202011~17、61,物联网工程专业班级为202071~202073,数据科学与大数据专业班级为202081~82
- 每个班级学号后两位为01~40
我使用的方法是str.substring分别对每四位为一段的数据进行校验,str.substring(0,4);str.substring(4,6);str.substring(6,8);
str.substring(0,4).matches("(2020(\\d{4}))");str.substring(4,6).matches("(11|12|13|14|15|16|17|61|71|72|73|81|82)[\\d]{2}");^([1-9]||[1-3][0-9]||40)$
这样分段进行校验比较方便。
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。
它包括两个类:Pattern和Matcher 。
Pattern: 一个Pattern是一个正则表达式经编译后的表现模式。
Matcher: 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查。
首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作。
Pattern的方法如下:
static Pattern compile(String regex)
将给定的正则表达式编译并赋予给Pattern类
static Pattern compile(String regex, int flags)
同上,但增加flag参数的指定,可选的flag参数包括:CASE INSENSITIVE,MULTILINE,DOTALL,UNICODE CASE, CANON EQ
int flags()
返回当前Pattern的匹配flag参数.
Matcher matcher(CharSequence input)
生成一个给定命名的Matcher对象
static boolean matches(String regex, CharSequence input)
编译给定的正则表达式并且对输入的字串以该正则表达式为模开展匹配,该方法适合于该正则表达式只会使用一次的情况,也就是只进行一次匹配工作,因为这种情况下并不需要生 成一个Matcher实例。
String pattern()
返回该Patter对象所编译的正则表达式。
String[] split(CharSequence input)
将目标字符串按照Pattern里所包含的正则表达式为模进行分割。
String[] split(CharSequence input, int limit)
作用同上,增加参数limit目的在于要指定分割的段数,如将limi设为2,那么目标字符串将根据正则表达式分为割为两段。
一个正则表达式,也就是一串有特定意义的字符,必须首先要编译成为一个Pattern类的实例,这个Pattern对象将会使用matcher()方法来生成一个Matcher实例,接着便可以使用该 Matcher实例以编译的正则表达式为基础对目标字符串进行匹配工作,多个Matcher是可以共用一个Pattern对象的。





题目集5(7-4)中Java集合框架应用的分析总结
HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序(这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),而且HashSet允许使用null 元素。HashSet是非同步的,如果多个线程同时访问一个哈希set,而其中至少一个线程修改了该set,那么它必须保持外部同步。 HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。HashSet的实现方式大致如下,通过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。
三、踩坑心得
日期类聚合问题中,最后两个日期间隔的测试点过不去,在eclipse中运行样例输出都能通过,但是在最后的测试点中出现错误,目前还没有分析出问题究竟出现在哪里,我的计算日期间隔的方法是求算出两个日期从开始到现在所拥有总天数,然后相减,思路是正确的,但是在具体实施过程中出现问题
正则表达式中:正则表达式稍有了解,只能设计一些相对简单的表达式进行匹配,复杂一点的可能就不太行了,尽管看了慕课和教材后边关于正则表达式的内容,自行设计表达式上稍有欠缺。
四、改进建议
通过画类图对类进行设计,先理清楚各个小问题之间的关系,再设计的类与类之间的关系。写出来的代码一般在逻辑上不会出现明显的问题,可能有时候还能通过样例测试,但这并不代表代码就是完善的。复杂多样的样例可以测试这个代码到底有没有算法上的错误,有助于我们纠错,完善代码,提高代码质量。
五、总结
通过作业逐步理解面向对象的封装性、继承性与多态性三大技术特性
封装性:
在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
继承性:
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。就是通过问题之间的联系创造父类与继承父类的所有属性以及方法的子类。它可以直接用,也可以重写父类的方法,还可以定义自己特有的成分。就像图形继承题,就是通过几何图形共有的面积,周长等特征来建立的联系,也可以去特别的求出它的数据所对应的立体图形的体积。
多态性:
多态性像是父类和子类的继承关系上的一种拓展,子类一定要具有抽象父类的方法。重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。重写式多态,也叫运行时多态。这种多态通过动态绑定技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。

浙公网安备 33010602011771号