java第2次大作业
前言
PTA题目集4一共三道题,总体的来说难度一般。考察的知识点有正则表达式、聚合关系和继承关系。
PTA题目集5一共四道题,总的来说难度稍难。考察的知识点有聚合关系、正则表达式和整型数组排序。
PTA题目集6一共六道题,总的来说难度偏简单。考察的知识点有正则表达式、继承和多态。
从上面可以看出这三个星期的题目考核的重点就是正则表达式,通过这几次作业我也的确明白了正则表达式的灵活多变,但是用正则去解决很多匹配的问题真的很方便,就是需要记的规则挺多的。
设计与分析
题目集4(7-2)和题目集5(7-4)的优劣比较
我们首先来看题目集4(7-2)


我们再来看题目集5(7-4),它们题目都相同只有聚合类型不同,图下是题目集5(7-4)的关系图

这两道题我都是根据题目所给的关系图进行编程的,由图可以看出两者聚合关系明显的区别。题目集4的是year与month聚合,month再与day聚合,day最后再聚合DateUtil,在编程时这四个类就是以如下方式编写
1 //Year类 2 class Year{ 3 private int year; 4 ....... 5 } 6 //Month类 7 class Month{ 8 private int month; 9 private Year year; 10 ........ 11 } 12 //Day类 13 class Day{ 14 private int day; 15 private Month month; 16 private int[] mon_maxnum ={31,28,31,30,31,30,31,31,30,31,30,31}; 17 ......... 18 } 19 //DateUtil类 20 class DateUtil{ 21 private Day day; 22 ........ 23 }
这种聚合方式有些像套娃,一个套一个,在写的过程中我是觉得这种聚合方式挺繁杂的,因为你要取Year的值的时候要先调Day取Month再用这个Month去取Year的值。题目集5的聚合方式是Day、Month、Year分别与DateUtil聚合,我根据这个聚合关系写出来的类如下
1 class DateUtil{ 2 private Year year; 3 private Month month; 4 private Day day; 5 private int[] mon_maxnum = {31,28,31,30,31,30,31,31,30,31,30,31}; 6 ....... 7 } 8 class Year{ 9 private int value; 10 ....... 11 } 12 class Month{ 13 private int value; 14 ....... 15 } 16 class Day{ 17 private int value; 18 ....... 19 }
可以看出Year、Month、Day三个类是分离开来的,这样取值就很方便,做数据处理的时候就会好很多我觉得。我们再来看看将我写的这两个日期类聚合设计编程代码分别在SourceMonitor上的运行结果(左图为题目集4,右图为题目集5)


对比两个得到的图,我们可以看到右图的题目集5平均复杂度还是有所下降的。这两种聚合方式,我就像题目上写的一样把题目集4的聚合方式称作聚合一,把题目集5的聚合方式称作聚合二。在我编写代码的过程中,我觉得两者的优劣为:聚合一方式去取Year、Month和Day的数值很繁复,但是这样写可以分担一些数据处理类DateUtil要做的事情,使得DateUtil类的代码不会很长;聚合二方式很容易取得Year、Month和Day的值,但是因为这三个类中除封装以外没有别的功能,使得数据处理类DateUtil类要写很多很多得代码来处理这三种数据。但是经过学习,我觉得还是聚合二比较好,因为我觉得它可以通过写更多的函数去分解,要添加类或者修改类都相对更容易,所以我觉得聚合二的聚合方式更好。
题目集4(7-3)、题目集6(7-5,7-6)三种渐进式图形继承设计思路与技术运用
首先我们来看题目

读题可知是要创建一个无属性实体父类Shape,然后Circle、Rectangle类继承Shape类并重写父类的面积输出函数,再Ball、Box类分别继承Circle、Rectangle类再次重写父类面积输出函数。大概等于Shape是”爷爷“,Circle、Rectangle是”爸爸“,Ball、Box是”儿子“这样的关系。运用了封装,继承和多态。子类都是在父类属性的基础上添加属性,属性我都用了private进行封装,改写父类方法体现了多态。可见如下类代码:
1 class Shape{ 2 Shape(){ 3 System.out.println("Constructing Shape"); 4 } 5 public double getArea() { 6 return 0.0; 7 } 8 } 9 class Circle extends Shape{ 10 private double radius; 11 12 Circle(){ 13 System.out.println("Constructing Circle"); 14 } 15 ......... 16 public double getArea() { 17 double area = radius *radius *Math.PI; 18 return area; 19 } 20 } 21 class Rectangle extends Shape{ 22 private double width; 23 private double length; 24 25 Rectangle(){ 26 System.out.println("Constructing Rectangle"); 27 } 28 .......... 29 public double getArea() { 30 double area = width * length; 31 return area; 32 } 33 } 34 class Ball extends Circle { 35 36 Ball(){ 37 System.out.println("Constructing Ball"); 38 } 39 public double getArea() { 40 return 4*super.getArea(); 41 } 42 public double getVolume() { 43 double volume = 4.0/3.0*super.getRadius()*super.getArea(); 44 return volume; 45 } 46 } 47 class Box extends Rectangle{ 48 private double height; 49 50 Box(){ 51 System.out.println("Constructing Box"); 52 } 53 ......... 54 public double getArea() { 55 double area; 56 area = 2*(super.getArea() + this.height*super.getLength() + this.height*super.getWidth()); 57 return area; 58 } 59 public double getVolume() { 60 double volume = this.height*super.getArea(); 61 return volume; 62 } 63 }
再来看7-5题目


读题可知是要建一个抽象父类,抽象父类没有属性只有方法,下面三个子类是实体子类都有自己的私有属性,从类图上看它们没有方法,但是事实上它们都继承了抽象父类的方法,并且因为每个图形算面积的公式和判定违法输入的标准不一样,所以对父类的方法进行重写。抽象类不可以new和实例化,定义的方法是抽象方法,子类必须重写所有的抽象方法才是实体类,要不然也是抽象类。体现了封装,继承,多态的设计特点。具体类设计代码如下
1 class Shape{ //图形类 2 3 public double getArea() { 4 return 0.0; 5 } 6 public boolean validate() { 7 return true; 8 } 9 public String toString() { 10 return super.toString(); 11 } 12 } 13 class Circle extends Shape{ //圆类 14 private double radius; 15 16 Circle(){} 17 Circle(double r){ 18 this.radius = r; 19 } 20 21 public double getRadius() { 22 return radius; 23 } 24 public void setRadius(double radius) { 25 this.radius = radius; 26 } 27 28 public double getArea() { 29 return radius*radius*Math.PI; 30 } 31 public boolean validate() { 32 if(radius > 0) 33 return true; 34 return false; 35 } 36 public String toString() { 37 String area = new java.text.DecimalFormat("0.00").format(getArea()); 38 return area; 39 } 40 } 41 class Rectangle extends Shape{ //长方形类 42 private double width; 43 private double length; 44 45 Rectangle(){} 46 Rectangle(double x,double y){ 47 this.width = x; 48 this.length = y; 49 } 50 51 public double getWidth() { 52 return width; 53 } 54 public void setWidth(double width) { 55 this.width = width; 56 } 57 public double getLength() { 58 return length; 59 } 60 public void setLength(double length) { 61 this.length = length; 62 } 63 64 public double getArea() { 65 return this.width*this.length; 66 } 67 public boolean validate() { 68 if(this.length >=0 && this.width>=0) 69 return true; 70 else 71 return false; 72 } 73 public String toString() { 74 String area = new java.text.DecimalFormat("0.00").format(getArea()); 75 return area; 76 } 77 78 } 79 class Triangle extends Shape{ //三角形类 80 private double sider1; 81 private double sider2; 82 private double sider3; 83 84 Triangle(){} 85 Triangle(double x,double y,double z){ 86 this.sider1 = x; 87 this.sider2 = y; 88 this.sider3 = z; 89 } 90 91 public double getSider1() { 92 return sider1; 93 } 94 public void setSider1(double sider1) { 95 this.sider1 = sider1; 96 } 97 public double getSider2() { 98 return sider2; 99 } 100 public void setSider2(double sider2) { 101 this.sider2 = sider2; 102 } 103 public double getSider3() { 104 return sider3; 105 } 106 public void setSider3(double sider3) { 107 this.sider3 = sider3; 108 } 109 110 public double getArea() { 111 double p = (this.sider1 + this.sider2 + this.sider3)/2.0; 112 //海伦公式 Math.sqrt(s*(s-a)*(s-b)*(s-c)) 113 double area = Math.sqrt(p*(p-this.sider1)*(p-this.sider2)*(p-this.sider3)); 114 return area; 115 } 116 public boolean validate() { 117 if(this.sider1<0 || this.sider2<0 || this.sider3<0) 118 return false; 119 //两边之和大于第三边 120 if(this.sider1 + this.sider2 <= this.sider3 ||this.sider1+this.sider3<=this.sider2 ||this.sider2+this.sider3 <=this.sider1) 121 return false; 122 return true; 123 } 124 public String toString() { 125 String area = new java.text.DecimalFormat("0.00").format(getArea()); 126 return area; 127 } 128 }
再来看7-6的题目

由类图可知,是使用了一个接口,无属性,只有一个抽象的方法,由实体去实现具体接口方法,用implements去使用接口。接口有许多的好处,接口就像是我们平时用的U盘一样,它的改变并不会影响其他的类,像U盘一样插上里面有什么就读什么出来。还有就是就是java不支持多重继承,但是可以实现多个接口,这个在某种程度上可以看做进行多重继承的一种办法。体现了封装、继承、多态、接口的设计特点。具体类设计如下
1 //接口 2 interface GetArea{ 3 public double getArea(); 4 } 5 class Circle implements GetArea{ 6 private double radius; 7 8 Circle(){} 9 Circle(double x){ 10 this.radius = x; 11 } 12 public double getRadius() { 13 return radius; 14 } 15 public void setRadius(double radius) { 16 this.radius = radius; 17 } 18 public double getArea() { 19 return this.radius*this.radius*Math.PI; 20 } 21 } 22 class Rectangle implements GetArea{ 23 private double width; 24 private double length; 25 26 Rectangle(){} 27 Rectangle(double x,double y){ 28 this.width = x; 29 this.length = y; 30 } 31 public double getWidth() { 32 return width; 33 } 34 public void setWidth(double width) { 35 this.width = width; 36 } 37 public double getLength() { 38 return length; 39 } 40 public void setLength(double length) { 41 this.length = length; 42 } 43 public double getArea() { 44 return this.length*this.width; 45 } 46 }
现在我们将这三种图形继承设计用SourceMonitor运行来看看(从左到右分别为题目集4(7-3),题目集6(7-5、7-6))



由图可见三个都是main函数复杂度最高,那是因为有判断输入的是是什么图形使用了switch case,所以复杂度较高,也许可以将输入写成函数去让mian调用来降低mian函数的复杂度。比较一下使用接口的最高复杂度最低,我感觉用接口也更好,因为使用接口更灵活也不会受到限制,可以随意更改接口中的方法,不会对实体类有影响,且接口可以使用很多个,在现实生活中的一些编码用父类的话可能会出现有某事物即继承A又继承B这样的情况,但是在Java中一个子类只能继承一个父类,所以我认为接口使用限制更少更好用些,以后应该尽量多用接口。
对三次题目集中用到的正则表达式技术的分析总结
这三次题目集中考核最多的就是正则表达式了,有题目集4(7-1)、题目集5(7-4)、题目集6(7-1、7-3、7-4)。其中题目集6所考核的正则表达式都是一些很基础的正则表达式,7-1检验QQ号,7-3检验验证码,7-4检验学号,都是一些简单的对整型字母的匹配正则表达式分别如下,去用matches()去匹配正则表达式
//7-1的正则表达式 String rexp = "[1-9][0-9]{4,14}"; //7-3的正则表达式 String A = "([0-9]|[a-zA-Z]){4}"; //7-4的正则表达式 String A = "[2020]([1][1-7]|[6][1]|[7][1-3]|[8][1-2])([1-9]|[2-3][0-9]|[4][0])";
题目集4(7-1)是对水文数据校验输出格式进行检验,数据结构如下

我的解题思路是一行一行的进行判断,用StringBuilder,一行行输入,每行存进StringBuilder类时在行末加个换行符,输入完成后用split("\n")将每行存入数组,错误的行就是数组下标加一,一行首先经过split("\\|")将数据分成5个部分存进一个数组中,如果没有分成五部分就判为错误,若正确再根据所给的数据结构,将数组的每个项再进行一一判断,比如第0项应该先进行split("/"),又可以分为三个部分再存入另外一个数组中再进行判断,数据出错就记下数组下标,注意一下开度的两个数是算两列,然后下标加一就是错误数据所在的列,将其输出就好。就这样进行一一判断,我用正则的地方其实是在年月日的地方,如下,也许可以整个用正则但是那对我来说可能要求太高了,我不太能实现,我写这个已经写了很久了修改bug也修改了很久。
public boolean validataMeasureDateTime(String measureDataTime) { //检测时间格式 int year,month,hour,flag = 0; String[] date = measureDataTime.split("/"); // System.out.println(date[0]); if(date.length == 3) { if(date[0].matches("[1-9][0-9]{0,3}")) { //年 year = Integer.parseInt(date[0]); if(date[1].matches("[1-9]|[1][0-2]")) { //月 month = Integer.parseInt(date[1]); date[2] = date[2].trim(); String[] _date = date[2].split(" "); // System.out.println(_date.length); if(month == 2){ if(year%400 == 0 || (year%4==0 && year%100!=0)){ //闰年 if( _date[0].matches("[1-9]|[1-2][0-9]") ) flag = 1; } else{ if( _date[0].matches("[1-9]|[1-2][0-8]") ) flag = 1; } } else if(month == 1||month == 3||month ==5||month ==7|| month == 8||month==10||month == 12) if(_date[0].matches("[1-9]|[1-2][0-9]|[3][0-1]")) flag =1; else flag =0; else if(_date[0].matches("[1-9]|[1-2][0-9]|[3][0]")) flag =1; if(flag == 1) { String[] time = _date[_date.length-1].split(":"); // System.out.println(_date.length); // System.out.println(time[0]); if(time[0].matches("[0-9]|[1][0-9]|[2][0-3]")){ hour = Integer.parseInt(time[0]); if(hour%2==0 && time[1].matches("[0][0]")) return true; } } } } } // System.out.println("Row:"+(this.row+1)+",Coulumn:1Wrong Format"); return false; }
题目集5(7-4)是统计Java源码中的关键字出现的次数

这道题我并没有全对,但是根据题目我对注释和字符串的处理和水文检测类似,一行一行的去检测用replaceAll函数将注释和字符串中的代码用空格替代。匹配关键用一个数组按升序储存53个关键字,然后用pattern去不断循环匹配,用另外一个数组先全部初始化为0,每检测到一次就给对应关键字数组下标的对应的数组的值加一。
对题目集5(7-4)中java集合框架应用的分析和总结
题目和解法思路都在对三次题目集中用到的正则表达式技术的分析总结中写完了,这边我就直接用SourceMonitor来运行一下

由图可以看出用正则的方式还是很简便的,复杂度都没有特别高,但是因为我计次数有创建了一个数组,所以在一些情况下内存的浪费还是有的。
踩坑心得
所有写的题目里,水文检测是我写的最长的时间的题目,有个测试点一直过不去,求助于班上一名大佬花了一个下午才修对测试点,修测试点的过程中更正了许多存在的漏洞,下面就来谈谈这些错误。首先是错误输出,这题的错误有两种,一种是整行错误,另一种是一行中的某列错误。我一开始写的代码是先输出所有的行错误再输出所有的列错误,但事实上它应该是一行行输出,于是我将代码进行了调整使得它能够一行行的对应着去输出错误
1 if(hangmax != -1 || liemax != -1) { 2 for(int i = 0 ; i < hang.length; i++) { 3 if(wrongHang[i] == 1) { 4 System.out.println("Wrong Format"); 5 if( hangmax > liemax && i == hangmax) 6 System.out.print("Data:"+ hang[i]); 7 else 8 System.out.println("Data:"+ hang[i]); 9 } 10 else { 11 int _flag = 1; 12 for(int j = 0; j < 6 ; j++) { 13 if(wrongLie[i][j] == 1) { 14 _flag = 0; 15 switch(j) { 16 case 0: 17 System.out.println("Row:"+(i+1)+",Column:1Wrong Format"); 18 break; 19 case 1: 20 System.out.println("Row:"+(i+1)+",Column:2Wrong Format"); 21 break; 22 case 2: 23 System.out.println("Row:"+(i+1)+",Column:3Wrong Format"); 24 break; 25 case 3: 26 System.out.println("Row:"+(i+1)+",Column:4Wrong Format"); 27 break; 28 case 4: 29 System.out.println("Row:"+(i+1)+",Column:5Wrong Format"); 30 break; 31 case 5: 32 System.out.println("Row:"+(i+1)+",Column:6Wrong Format"); 33 break; 34 } 35 } 36 } 37 if(_flag == 0) { 38 if(liemax > hangmax && liemax == i) 39 System.out.print("Data:"+ hang[i]); 40 else 41 System.out.println("Data:"+ hang[i]); 42 } 43 } 44 } 45 }
然后的错误在数据第一个部分中间可能也有空格然后我使用了空格分开这个时候就会出现数组超限,我针对这个也进行了调整。
1 date[2] = date[2].trim(); 2 String[] _date = date[2].split(" ");
每次用空格分隔时先使用trim去首尾空格,这里是去每个经过/划分的小部分也都使用它去首尾。然而这改了这些还是不能通过测试点,最后发现原来是测试水位这个数据的正则出错了,首先正则表达式中"."是有它自己的意义的所以要使用转义符\\转为普通的.才行,然后就是后面应该是[0-9]{1,3},一开始我理解错了意思,我以为不能够".000"这样,但事实上它是可以的,改正了错误之后就通过测试点了,可见写正则表达式的时候真的要小心谨慎,毕竟在这种有很多需要判定的代码中真的不容易检查出来
1 if(waterLevel.matches("[1-9][0-9]{0,2}(\\.[0-9]{1,3})?")) 2 return true;
改进建议
还是一样吧,我用SourceMonitor运行了所有的代码,显示都是主函数的复杂度是最高的,按理来说主函数是要尽量简洁的,需要进行的操作应该放到类里面用类的方法实现,来降低主函数的复杂度。也确实需要改,应该别人看代码都是从主函数看起,主函数那么一大段就可读性很低,如果主函数仅有几个意思明显的类和类方法,你写代码的思路别人看起来就很清晰明了,还是需要多改进。然后就是正则吧,真的非常多的内容正则,不愧老师说正则可以单独拉出来做一门课程,内容真的非常之多,但是用在需要使用匹配的地方也非常的方便,还是需要多多掌握。
总结
这几次的题目让我学到了很多关于正则表达式和排序的知识,了解到了关于Comparator函数的一些知识,它是排序自带的函数,改写这个函数就可改变排序的方式。
剩下的和上次还是一样的,对老师就是希望老师能把上课用于讲课的那个知识点文件发给我让我瞅瞅研究研究,然后课程实验作业什么的能够更多的要求类上面的设计以及可读程度。课后可以组织一些学习任务比如让几个人一起成为一个小组一起做一个Java小游戏或者小项目什么的,每个人写一、两个类这样然后写完大家合在一起相互阅读代码,有人觉得谁的代码复杂就大家一起讨论进行修改,最后看看运行效果如何,可以和别的组交换成果大家相互寻找bug这样。当然这仅是我个人的意见,感觉会很有意思如果时间充足的话。

浙公网安备 33010602011771号