第二次OOP课程作业总结
一.前言
此阶段考察的多是对类的相关内容,如聚合、继承。相较于上一阶段的表达式求导对正则表达式的运用,此阶段侧重点为进行代码复用和扩展,也更加利于理解面向对象编程的思想,收获不小。当然这阶段仍有不足之处,pta作业有两次写得比较仓促,各有一题没能得到满分,可从结果来看自己是不够满意的,争取以后的作业中不再出现这种问题。说了这么多还没有进入正题,下面分别对三次作业做个简要分析。
(一)第四次题目集:题目量仅有三道题。第一题难度较大,需要考虑数据的存储方式以及正则表达式的书写。第二题和第三题难度不高,考察的是JAVA的一些基本语法和继承的相关知识。
(二)第五次题目集:题目量有五道题,相对第四次题目总体难度有略微降低。前三题是基础简单题,难度较低。第四题难度略高,考察的是对输入输出做处理修改的能力和对题意得理解能力。第五题难度不高,因为有了前一次日期聚合题目的经验,这道题并没有花费多少时间。
(三)第六次题目集:题目量有六道题,难度不高,主要考察的是简单正则表达式的书写和继承的相关知识。
二.各题目的设计与分析、采坑心得及改进建议:
1.题目集4: 7-2 日期问题面向对象设计(聚合一)
题目设计


1. 该题目需要我完成三个功能分别是:求下n天,求前n天,求两个日期相差的天数。所以,根据题意,在主类中我先写了一个switch-case根据第一输入的数字不同调用相应的实现功能的方法,下面以case1为例。
1 case 1:
2 int n1 = in.nextInt();
3 if(date.checkInputValidity()) {
4 date.getNextNDays(n1);
5 System.out.println(date.showDate());
6 }
7 else {
8 System.out.println("Wrong Format");
9 System.exit(0);
10 }
11 break;
2.再根据题目给出的类题,定义四个类,分别为名为DateUtil、Day、Month、Year,,由后往前编写相应代码。
3.类Year、类Month和类Day中的方法都较为简单,其中相对较为的方法是类Year中的方法isLeapYear,根据对闰年判断的定义(年份是4但不是100的倍数或者是400的倍数),编写如下展示的代码。
1 public boolean isLeapYear() {
2 boolean isLeap = false;
3
4 if(value % 4 == 0 && value % 100 != 0 || value % 400 == 0)
5 {
6 isLeap = true;
7 }
8 return isLeap;
9 }
4.类DateUtil中的方法较为复杂,下面对几个重要方法作出解释。
1 public DateUtil getNextNDays(int n) {
2 while(n + day.getValue() > day.mon_maxnum[day.getMonth().getValue()-1]) {
3 if(day.getMonth().getYear().isLeapYear()) {
4 day.mon_maxnum[1] = 29;
5 }
6 else {
7 day.mon_maxnum[1] = 28;
8 }
9 if(day.getMonth().getValue() < 12) {
10 n = day.getValue() + n - day.mon_maxnum[day.getMonth().getValue()-1];
11 day.getMonth().monthIncrement();
12 day.setValue(0);
13 }
14 else if(day.getMonth().getValue() == 12) {
15 n = day.getValue() + n - day.mon_maxnum[day.getMonth().getValue()-1];
16 day.getMonth().getYear().yearIncrement();
17 day.getMonth().resetMin();
18 day.setValue(0);
19 }
20
21 }
22 day.setValue(n+day.getValue());
23
24 return new DateUtil(day.getValue(), day.getMonth().getValue(), day.getMonth().getYear().getValue());
25 }
该方法的功能是求下N天,首先做一个while循环,若n大于本月天数则令n减去本月天数并使月数加一;当month为12时,令month重置为1,year加1;,直到n小于本月天数,此时n为n天后的day。
1 public DateUtil getPreviousNDays(int n ) {
2 for (int i = 0; i < n; i++) {
3 day.dayReduction();
4 if(day.getMonth().getYear().isLeapYear()) {
5 day.mon_maxnum[1] = 29;
6 }
7 else {
8 day.mon_maxnum[1] = 28;
9 }
10 while (day.getValue() < 1) {
11 day.getMonth().monthReduction();
12 if (day.getMonth().getValue() < 1) {
13 day.getMonth().resetMax();
14 day.getMonth().getYear().yearReduction();
15
16 }
17 day.resetMax();
18 }
19 }
20 return new DateUtil(day.getValue(), day.getMonth().getValue(), day.getMonth().getYear().getValue());
21 }
该方法的功能是求前N天,嵌套了两个循环,使n减1的同时使day减1,当day小于1时使month减1并使day的值改为上个月最大天数,当month小于1时使year减1并使month的值改为12,直到循环结束。
1 public int getDaysofDates(DateUtil date) {
2
3 int sum1 = 0;
4 for(int i = 1; i < date.getDay().getMonth().getValue(); i++) {
5 if(date.getDay().getMonth().getYear().isLeapYear()) {
6 date.getDay().mon_maxnum[1] = 29;
7 }
8 sum1 += date.getDay().mon_maxnum[i-1];
9 }
10
11 sum1 += date.getDay().getValue();
12
13 int sum2 = 0;
14
15 for(int i = 1; i < day.getMonth().getValue(); i++) {
16 if(day.getMonth().getYear().isLeapYear()) {
17 day.mon_maxnum[1] = 29;
18 }
19 sum2 += day.mon_maxnum[i-1];
20 }
21
22 sum2 += day.getValue();
23
24 int sum3 = 365 * (date.getDay().getMonth().getYear().getValue() - day.getMonth().getYear().getValue());
25
26 while(date.getDay().getMonth().getYear().getValue() > day.getMonth().getYear().getValue()){
27 if(day.getMonth().getYear().isLeapYear()) {
28 sum3++;
29 }
30 day.getMonth().getYear().yearIncrement();;
31 }
32 return Math.abs(sum1 - sum2) + sum3;
该方法的功能是求两个日期相差的天数,首先先计算两个日期的年和月分别转换为sum1、sum2、sum3,最后返回|sum1-sum2|+sum3,此值即为两个日期相差的天数。
题目分析:
因为在前几次题目集有做过与处理日期相关的问题,这道题对在实现三个功能的方法的编写并不困难,难点是对面向对象编程思维的运用。合理运用面对象对象编程的思维,可以将题目的设计化繁为简,按题目中的类图把每个类的中方法功能完善后和变量定义完成后,再在主类中编写调用对应功能的方法便是件轻而易举的事情。
踩坑心得:
在该题目中,因为聚合的奇怪方式,初看题时如何调用Day、Month、Year的方法着实让我头痛了一会儿。在理解正确的写法后,采用了如day.getMonth().validate()的方式调用想用的方法后,该问题困难应迎刃而解。
还有的是在处理三个功能的方法中,初次写时未考虑到在循环的年份中有闰年的情况导致天数的计算总是会有几天的相差。因此我选择在每次对年份有修改时,调用方法isLeapYear,对年份是否为闰年进行判断后对该年的二月份的天数进行修改。
改进建议:
由PowerDesigner和SourceMonitor和的生成报表可以看出,此题代码存在着几个类相互耦合度较高和复杂度偏高的问题。复杂度高的原因是对天数处理的方法中的循环,尤其是求前n天的方法中多个循环相互嵌套导致复杂度十分高。应该采用类似求下n天的方式,尽可能减少循环的嵌套。
2.题目集5: 7-5 日期问题面向对象设计(聚合二)
题目设计


题目分析:
该题与日期问题面向对象设计(聚合一)有关方法的实现大体相同,主要改变的对代码聚合方式的改变。
踩坑心得:
因为是日期问题面向对象设计(聚合一)的基础上做的修改,因此在改变聚合方式时并没有出现错误。
只有一个有关下n天的整型数最大值测试的测试点无法通过,经用debug功能检查,发现问题出在getNextNDays方法中的while(n + day.getValue()> mon_maxnum[month.getValue()-1]。当n取整数型的最大值时,while循环符合大于的要求但无法进入while,于是在大于号两端同时减一个数。经修改后,while循环可以在n取整数型最大值时正常运行。
改进建议:
由PowerDesigner和SourceMonitor和的生成报表以及pta的测试点耗时可以看出,此题代码存在着几个类相互耦合度较高和复杂度偏高的问题。主要问题是求前n天的方法中多个循环相互嵌套导致复杂度十分高,可以采用类似求下n天的方式,尽可能减少循环的嵌套。

日期问题面向对象设计(聚合一)和日期问题面向对象设计(聚合二)的优劣比较:
运行时间(效率):
由两次测试点的耗时可以看出,聚合方式不同的大体耗时时相同,只是聚合二的测试点由当n取整数型的最大值的情况出现,会导致求前n天时耗时较高。
(聚合一)

(聚合二)

复用性:
由PowerDesigner生成的类图可以看出,聚合二的耦合性远远低于聚合一,因此聚合二的复用性会高于聚合一。原因是在聚合一中,在类DateUtil中定义Day,在类Day中定义Month,在Month中定义Year,这样相互嵌套的方式,会使对数据处理时需要频繁使用类似day.getMonth().getYear().getValue() 的方式调用类中的方法,导致类与类之间的关系大大增强。而聚合二中,是在类DateUtil中同时定义了Day、Month和Year,此方法减少了Day、Month和Year三个类的相互调用,大大减少了耦合度。
可读性:
聚合一定义Day、Month和Year三个类的方式较为繁琐,会导致大量的类day.getMonth().getYear().getValue() 的方式调用类中的方法,使得代码不简洁,甚至可以说是令人眼花。而聚合二在类DateUtil中同时定义了Day、Month和Year三个类Day、Month和Year三个类的相互调用,所有的数据处理统一在类DateUtil中处理,使得代码的简洁度大大提高,可读性也显著增加。
3.题目集4: 7-3 图形继承
题目设计:


先定义Shape、Circle、Rectangle、Ball、Box五个类,再按题意所需继承,并在在每个类中定义求面积的方法和求体积的方法,需要手动编写的是求面积的方法,难度不大,可以直接return利用面积的公式的面积答案。
题目分析:
此题考察的对封装和继承的理解,很基础的一道题目。
踩坑心得:
由于第一次做父类继承的题,一开始在方法重写时不知如何正确理解类与类继承的关系。经过学习了解super和this的区别和用法后,可以正常调用父类的方法。
改进建议:
可以将Shape定义为抽象类,并做一个含有求面积方法的接口,使Circle、Rectangle、Ball、Box按所需继承,可以提高代码的复用性,也可以使代码更简洁。
4.题目集6: 7-5 图形继承与多态
题目设计:


首先按题意定义Circle、Rectangle及Triangle三个类,并使其继承自抽象类Shape。在类Shape中定义求面积的方法、判断数据合法性的方法和将数据转化字符串方法,利用简单的数学公式即可解决,难度不大。
随后在主类中用ArrayList<Shape> list = new ArrayList<Shape>()语句和for循环储存每个图像,依次判断图像的类型并进行对应图像的方法调用(如下)。
1 int num1 = in.nextInt();
2 int num2 = in.nextInt();
3 int num3 = in.nextInt();
4 ArrayList<Shape> list = new ArrayList<Shape>();
5 Shape t;
6 //输入错误情况判断
7 if(num1 < 0 || num2 < 0 || num3 < 0) {
8 System.out.print("Wrong Format");
9 System.exit(0);
10 }
11
12 //存入面积
13
14 //圆形
15 for(int i = 0; i < num1; i++) {
16
17 double r = in.nextDouble();
18 t = new Circle(r);
19 if(!t.validate()) {
20 System.out.print("Wrong Format");
21 System.exit(0);
22 }
23 else {
24 list.add(t);
25 }
26 }
27
28 //矩形
29 for(int i = 0; i < num2; i++) {
30
31 double l = in.nextDouble();
32 double w = in.nextDouble();
33 t = new Rectangle(l,w);
34
35 if(!t.validate()) {
36 System.out.print("Wrong Format");
37 System.exit(0);
38 }
39 else {
40 list.add(t);
41 }
42 }
43
44 //三角形
45 for(int i = 0; i < num3; i++) {
46
47 double l1 = in.nextDouble();
48 double l2 = in.nextDouble();
49 double l3 = in.nextDouble();
50 t = new Triangle(l1,l2,l3);
51
52 if(!t.validate()) {
53 System.out.print("Wrong Format");
54 System.exit(0);
55 }
56 else {
57
58 list.add(t);
59 }
最后按题意所需方式输出原序和排序后的数据。
题目分析:
相比上一题,此题多做额外要求——掌握类的多态性及其使用方法,难度不大。
踩坑心得:
起初没有仔细阅读实验指导书,对数据处理的方法是先计算出答案后存入一个数组再进行输出,没有用上ArrayList。经修改后先用ArrayList<Shape> list = new ArrayList<Shape>储存图形的类型后再对存储在每个Shape中的特定图形数据进行处理后再输出。
改进建议:
可以额外定义一个接口来实现求面积方法,可以提高代码的复用性,也可以使代码更简洁、可读性增加。
5.题目集6: 7-6 实现图形接口及多态性
题目设计


定义类Circle及类Rectangle并按图形要求定义相关的私有变量,再在这两个类中实现GetArea中getArea方法。
题目分析
相比上两题,此题多做额外要求——掌握接口使用方法,仅需将求面积的方法放在接口中即可,难度不大。
踩坑心得
无,有了前两次渐进式图形继承设计题目的经验,这道题目处理起来难度不大,并没有遇上难点。
改进建议
无
正则表达式技术的分析总结
水文数据校验及处理
此题对正则表达式的运用主要体现在对数据合法性的检测中,如下。
//采集时间合法性检测
Pattern p = Pattern.compile("((([1-9]{1}|[1-9][0-9]{1}|[1-9][0-9]{2}|[1-9][0-9]{3})"
+ "/((([13578]|1[02])/([1-9]|[12][0-9]|3[01]))|(([469]|11)/([1-9]|[12][0-9]|30))"
+ "|(2/([1-9]|[1][0-9]|2[0-8]))))|((([1-9]{0,1}[0-9]{0,1})(0[48]|[2468][048]|[13579][26])"
+ "|((0[48]|[2468][048]|[3579][26])00))/2/29)) ([02468]|1[02468]|2[02]):00");
//水位、流量合法性检测
Pattern p = Pattern.compile("^[0-9]+(.[0-9]{1,3})?$");
要求:
1.测量时间:格式为“年/月/日 时:分”,其中年份取值范围为[1,9999],“年”非 4 位数时, 之前不加“0”,“月”与“日”为一位数时之前也不加“0”,日期与时间之间有一个空格, “时”与“分”之间采用冒号分隔(英文半角),“时”为一位数时之前不加“0”,“分” 始终保持两位且始终为“00”。
2.目标水位、实际水位、流量:均为实型数,取值范围为[1,1000),小数点后保留 1-3 位小数 或无小数。
分析:
1.有关测量时间的正则表达式是表达每一位数上可以出现的数字以及其次数。
2.^表示匹配字符串开头,[0-9]+表示匹配整数部分,(.[0-9]{1,3})?表示匹配小数部分或无小数,$表示匹配字符串尾部。
统计Java程序中关键词的出现次数
此题对正则表达式的运用主要体现在对关键词的匹配和删去不用统计的字符串中,如下。
要求:
匹配关键词的次数
分析:
1.先用replace替换,;[]等符号
2.s = s.replaceAll("(//.*)"," ")的作用是替换双斜杠注释的情况;s = s.replaceAll("(/\\*{1,2}[\\s\\S]*?\\*/)"," ")的作用是替换/* */的注释情况,其中的\\s\|S是表示出现换行的情况。
3.Pattern p = Pattern.compile("\\b" + keyword[i] + "\\b")的作用是匹配关键词,keyword两端的\\b表示匹配的关键词两端是空格的情况。
//匹配关键词
Pattern p = Pattern.compile("\\b" + keyword[i] + "\\b");
//删去不用统计的字符串
s = s.replaceAll("(//.*)"," ");
s = s.replaceAll("(/\\*{1,2}[\\s\\S]*?\\*/)"," ");
s = s.replaceAll("(\"(.*?)\")"," ");
s = s.replace("["," ");
s = s.replace("]"," ");;
s = s.replaceAll(";"," ");
s = s.replace(","," ");
正则表达式训练-QQ号校验
要求:
1.要求必须是 5-15 位。
2.0 不能开头。
3.必须都是数字。
分析:
^表示匹配字符串开头,[1-9]{1}表示首个数字不为0,[0-9]{4,14}$表示匹配余下的四至十四位数字,$表示匹配字符串尾部。
Pattern p = Pattern.compile("^[1-9]{1}[0-9]{4,14}$");
正则表达式训练-验证码校验
要求:
接受给定的字符串,判断该字符串是否属于验证码。验证码是由四位数字或者字母(包含大小写)组成的字符串。
分析:
^表示匹配字符串开头,(\\d|\\w){4}表示匹配为字母或者数字的四个字符,$表示匹配字符串尾部。
Pattern p = Pattern.compile("^(\\d|\\w){4}$");
正则表达式训练-学号校验
要求:
1.1、2位:入学年份后两位,例如20年
2.3、4位:学院代码,软件学院代码为20
3.5位:方向代码,例如1为软件工程,7为物联网
4.6位:班级序号
5.7、8位:学号(序号)
分析:
^表示匹配字符串开头,20201[1-7]表示匹配软件学院1方向的学生,20207[1-3]表示匹配软件学院7方向的学生,20208[1-2]表示匹配软件学院8方向,0[1-9]|[1-3][0-9]|40表示匹配学生的学号,$表示匹配字符串尾部。
Pattern p = Pattern.compile("^(20201[1-7]|20207[1-3]|20208[1-2])(0[1-9]|[1-3][0-9]|40)$");
总结:
正则表达式的主要应用对象是文本,使用正则表达式可以指定想要匹配的字符串规则,然后通过这个规则来匹配、查找、替换或切割那些符合指定规则的文本。知识点并不多也不难,可想掌握需要长时间的学习和应用,希望以后还会有类似的训练。
题目集5(7-4)中Java集合框架应用的分析总结
//删去不用统计的字符串
s = s.replaceAll("(//.*)"," ");
s = s.replaceAll("(/\\*{1,2}[\\s\\S]*?\\*/)"," ");
s = s.replaceAll("(\"(.*?)\")"," ");
s = s.replace("["," ");
s = s.replace("]"," ");;
s = s.replaceAll(";"," ");
s = s.replace(","," ");
该题目首先通过正则表达式对不需统计的字符串进行清除替换为空格。并在清楚无效字符串后再用规则Pattern p = Pattern.compile("\\b" + keyword[i] + "\\b")和m.find对关键词的个数进行检测并输出。
改进建议
此题我没有得满分,我起初想原因可能是[]中的字符串处理问题,可是我去除[]后仍无法通过测试点,可能是对数据的储存方式出现了错误或者是没用理解题意。

三.总结课程收获
1.面向对象的编程思维
历经两个月的面向对象设计课程学习后,我自觉从小白到稍微入了点门,最大的收获就是了解到面向对象思想,并且能够类的方法,刚开始做作业感觉Java的很多内容和C语言很像,很多格式都差不多,但到了后面慢慢发现Java的东西还有很多,如继承,封装等毕竟跟C语言不一样,不是所有代码都写在一个主函数和几个子函数里的。再就是一个题目理解的层次更深,以前的重点是实现的过程,现在围绕对象不仅仅只有实现,完成某一个步骤,更需要在整个问题上更好的实现,而且在题目修改的情况下不需要改变过多的代码,并且程序也往往具有更好的可扩展性。当然现在的理解可能只是皮毛,更多的操作和思考还有待学习。
2.编程能力的提升
在这两周的学习中,我自觉学到的知识有很多,例如掌握Java语言中继承的基本概念及使用方法以及Java语言中super关键字的使用方法,掌握了Java语言中成员变量隐藏的基本概念及使用方法,最重要的是理解了继承和组合的差别,懂得了多态的使用,并理解了程序设计中代码复用的重要性,在继承中使用多态,可达到复用的效果,有以下多个好处;子类不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可,大大提高程序的可复用性;子类可以写相同的函数名降低了程序的耦合度,简化了定义的函数名;简化了子类子类的代码,提高了其使用效率。这些知识的学习,对自己的编程能力和debug能力得到了比较大的提升。
3.代码规范
此阶段在完成课程学习和编程作业的同时,相比第一阶段的对插件和工具的使用的生疏,我现阶段更加熟练了。PowerDesigner和SourceMonitor是很好的工具,如果能够将其辅助性工具利用好的话,它们将对实现设计任务带来极大的便利。
四.课程改进建议
1.作业难度
第二阶段的题目集难度相较第一次题目集难度有所下降,更多的是对面向对象的思维的培养,尤其是有关继承与多态的方面。
2.理论课程
通过这两个月的课程我有一些小小的建议,当然是对于我个人,就是偏简单的课堂作业的形式很好,能更好的巩固刚学的知识,防止下节课忘了上节课的内容,其实我也希望在课程中能讲解一下PTA作业中难一点的题目或者方法等,有的时候可能离做出来就差那一点提醒或是指导。
浙公网安备 33010602011771号