OOP第二次博客作业
OOP第二阶段作业总结
一、前言
题目来源于PTA题目集4-6,以下是对它在知识点,题量,难度上的一个总结。
题目集4:
-
知识点:7-1考察的是正则表达式的应用以及对字符串类的应用及数据计算处理。
7-2考察了类间关系-聚合的应用 。
7-3考察的主要是继承及方法的重写,代码可复用性得到了初步体验。
-
题量:由于在7-2给出了类图,7-3上课老师也一直在讲,因此耗费时间不多,但是7-1的正则表达式上在一定程度上增加了题目的题量。
-
难度:7-1对于我来讲难度很大,复杂的正则表达式我理不清楚。
7-2的难度在于聚合关系的应用 ,老师给的类图里面没有告诉函数的传参是什么,因此传参耗费了大量时间
7-3难度较低,题目本身较简单而且给了大量提示。
题目集5:
-
知识点:7-5主要也是聚合关系,但与前面7-2的聚合关系使用有点不一样。
7-4主要考察正则表达式以及接口的应用。
7-1 、7-2、 7-3主要考查数组,各种排序方法的练习
-
题量:题目数量虽然有所增加,但难度有所降低,且好几道题目前面做过了。总体来说题量不算大。
-
难度:本次题目难度主要集中于正则表达式的检验,如何将他们筛选出来,筛选出来以后如何放入字符串数组来遍历让map发挥作用。
题目集6:
-
知识点:7-1、7-3、7-4主要考察正则表达式的简单应用。
7-2考察字符排序 ,将字符串中字符传入数组再进行字符串的排序。
7-5主要考查了collections和Arrays的操作以及多态性封装性抽象类的使用。
7-6主要考察了类的封装性多态性和接口的应用。
-
题量:题目数量虽多,但难度较低,因此题量不大
-
难度:7-5对我来说有点难度,但是参考了下网上的代码后就没啥问题了。
二、设计与分析
本次BLOG的分析内容如下: 1、题目集4(7-2)、题目集5(7-5)两种日期类聚合设计的优劣比较
先分析老师给的类图:
题目集4(7-2)类图:

题目集5(7-5)的类图:

由类图可知,两者采用的都是聚合关系 。7-2采用的是一个套一个的聚合关系,7-5采用的是多个类聚合于一个类,聚合关系描述类之间存在整体和部分之间的关系,7-2的聚合关系在业务类当中进行了天数的计算,而计算不同的年,月,日就交给了不同的类去使用,实现了类的单一职责原则,dateutil类只处理了如何计算天数,但在类间调用、类间传参上很麻烦,需要一步一步去调用。7-5虽然没有实现单一职责原则,但简化了类间关系的调用。我当时没有注意到两者的区别,直接复制了7-2的代码进行修改,因此这两道题并没有按照老师要求的去做。但在总结的时候看到了这一层不同。
以下是两个的SourceMonitor分析图:
题目集4(7-2)分析图:

题目集5(7-5)分析图:

从分析雷达图中我们可以发现此次程序最大圈复杂度和最大深度,平均深度超过了合理的范围,通过对程序的分析发现原因可能是因为在判断输入值的合法性以及比较日期大小的方法中使用了大量的if-else结构,为了减少其圈复杂度,我们可以尝试通过以下方式进行改进:
1.提前return,减少else判断。
2.使用三目运算符。
3.使用switch case,同样,我们可以借鉴方式一提前return,来代替else。
因为这两道题只是聚合关系的不同 ,所有方法都是相似的 。个人认为在7-2采用的聚合方式可能在一定程度上降低圈复杂度, 提高代码的维护性。
2、题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等)
题目集4(7-3)类图:

由类间关系图可知 ,父类定义子类构建 Rectangle类与Circle类同继承于Shape类, 他们继承了父类当中getArea()的方法,Ball和Box又分别继承自Circle类和Rectangle类 Ball类继承了父类的属性及其方法 ,又增添了一个新的方法求体积 Box类除了继承了父类的属性和方法也定义了一个新的求体积的方法 ,这样对getArea()的覆写提高了代码的可维护性和可复用性 ,运用了封装 ,封装了属性, 保护它不被其它的函数意外的修改。
题目集4(7-3)分析图:

可以看到,平均复杂度,平均方法,和类均处在正常合理的范围之内,这也侧面体现出继承类的优点,若使用if-else或者switch进行判断后再进行后续操作,其圈复杂度肯定是不如继承优秀的,同时可读性也较低.因此我们在程序的编写中对于特定类以及方法,使用继承可以达到事半功倍的效果.
题目集6(7-5)类图:

由图可知 Circle类、Rectangle类和Triangle类都继承于一个父类Shape类 Shape类属于抽象类 是属于抽象类定义实体类 所以抽象类不能创建对象(即使没有抽象方法),抽象类定义的成员变量和方法标明其子类必须具有这些成员变量和该方法,那么抽象类的多个子类可以继承这些方法,也可以重写父类的方法 那么在本题当中 我虽然我没有用到toString的方法 但是也要重写父类的方法 抽象类当中并没有定义变量 所以子类中的变量都是私有的 实体类继承了抽象类的方法只需要进行覆写即可。
题目集6(7-5)分析图:

这个分析图也是奇怪,不知道为什么显示不出雷达图,看起来就没前面的直观。但是20的复杂度还是不低的。
题目集6(7-6)类图:

本题属于接口定义实现类构建。我定义一个接口,一个接口只有方法的特征没有方法的实现,Java接口必须通过类来实现它的抽象方法 。通过implements来实现 ,可以有不止一个实现类,那么对接口类方法进行重载一样的,在类内的属性均为私有属性,实现了封装。我只需要设计一个获得面积的接口 ,实现类Rectangle Circle进行方法的重载,在主函数当中进行多态的使用(也要注意多态的使用) ,就可以提高代码的可复用性, 也便于代码的维护 。
题目集6(7-6)分析图:

由于本题要处理的数据量较小,因此我们在设定for循环以及if-else结构时所设置的数量较少,因此雷达图给出的代码评估是较为合理的。
3、对三次题目集中用到的正则表达式技术的分析总结
正则表达式作为JAVA语言中不可或缺的重要一环,虽在书上的内容篇幅较少,但我们也不能忽视对其的学习以及应用,下面由简入难进行分析:
一、简单:题目集6当中的几个正则表达式属于简单的正则表达式 这里考取了几个知识点
1、如何限定输入字符串的长度(QQ号考点)
2、如何对输入字符串的种类进行检测 就是各个正则字符所代表的含义
3、“|”的含义考察(在学号和验证码考察占比比较重)
4、超过10的数字如何用正则表达式进行校验(学号1-40)
二、中等:题目集5当中找出关键词的正则表达式
1、本题的正则表达式的表达式并不是非常难当然也不简单 关键是思考检测什么才能将该剔除的关键字剔除该统计的关键字进行统计 所以最后我所选取的方法是对注释等进行正则表达式的匹配 因此正则表达式主要考查的是一些特殊字符的匹配 和多个不确定字符该如何匹配
三、困难:水文数据的检测
1、主要是考察时间日期的匹配 以及奇偶性的匹配 在时间日期的匹配很有难度 首先要考虑年月日的合理性 还有时间输入的合理性 年月日要考虑是否为闰年 考虑2月份天数 以及不满十天的输入格式 年份输入格式很复杂 稍有一步步细心可能就白匹配了
2、考察一些特殊字符的匹配 理解类似^ , $的含义以及使用位置便于使用与操作
四、三次题目集的正则表达式基本形式如下:
String str="(^[1-9]{1}[0-9]{0,3})(\\/)((1[0-2])|[1-9])(\\/)((3[0-1])|([12][0-9])|([0-9])) (([02468])|(1[02468])|(2[024]))(:)([0]{2})";//水文校验
String regex="[1-9][0-9]{4,14}";//qq号校验
String reg="[0-9a-zA-Z]{4}";//验证码校验
String reg="2020((1[1-7])|61|(7[1-3])|(8[1-2]))([0-3][1-9])|(40)";//学号校验
正则表达总结:
1、写正则表达式之前思路清晰尤为重要。
2、每一种可能输入的情况都要考虑到,选择合适的正则表达式进行匹配 ,例如qq号就要找正确输入, 而关键字查找就要匹配注释并将其删掉。
3、正则表达式本身并不难,复杂的是要检测的数据的匹配,匹配在一定程度上增加了正则表达式的难度,要熟练运用各种符号、操作符。在一些特定的输入方式中用最简洁的操作符来进行匹配。
4、题目集5(7-4)中Java集合框架应用的分析总结
List、Set或Map中选择任意一种,我所选择的是Map,之所以选择Map是因为Set当中不允许有重复元素,而输入的提示又是Map,而且Map可以HashMap的值是没有顺序的,他是按照key的HashCode来实现的,就是根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度。而且我进行筛选的时候无法确保是不是一个关键字只出现一次,所以我所选择的集合框架时map,通过值的增加,最后只需要统计值是多少就可以知道关键字的输出个数。
题目集5(7-4)复杂度分析图:

虽然java集合框架没什么问题,但是复杂度还是不低,究其原因,还是if-else的嵌套问题。
三、采坑心得
1.题目集4的7-2这道题,在计算两个日期之间相差的天数时。我刚开始想的是直接拿两个日期进行相减,例如A-B,但是这样的话就是计算A离今年1月1日的天数,B离B那一年最后一天的天数,但是不知道为什么,得出的结果一直答案错误。无奈之下,我就计算了A,B分别到各自年初的天数,然后用A年初减去B年初的天数加上A到A年初的天数减去B到B年初的天数,得到的就是A与B的天数差了。
部分源代码如下:
public int getDaysOfDates(DateUtil date){//求两个日期之间的天数 int count=0; int sumMonth=0; int[] monthMax ={0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; DateUtil x=date;//大 DateUtil y=this;//小 if (!this.compareDates(date)){ x=this; y=date; } if(this.equalTwoDate(date)){//如果两天的日期相等 return 0; } for (int i = y.getDay().getMonth().getYear().getValue(); i < x.getDay().getMonth().getYear().getValue(); i++) { if ((i% 4 == 0 && i % 100 != 0)|| i % 400 == 0) { count++; } } int sumYear = (x.getDay().getMonth().getYear().getValue() - y.getDay().getMonth().getYear().getValue()) * 365 + count; int sumMonth1=monthMax[x.getDay().getMonth().getValue()-1]+x.getDay().getValue(); if (x.getDay().getMonth().getYear().isLeapYear()&&x.getDay().getMonth().getValue()>2) { sumMonth1++; } int sunMonth2=monthMax[y.getDay().getMonth().getValue()-1]+y.getDay().getValue(); if (y.getDay().getMonth().getYear().isLeapYear()&&y.getDay().getMonth().getValue()>2) { sunMonth2++; } sumMonth=sumMonth1-sunMonth2; return sumMonth+sumYear; }
这道题还遇到了一个小问题,就是A-B可能是正数,也可能是负数,所以当时负数时,两者就得交换相减才能得出正确答案。
DateUtil x=date;//大 DateUtil y=this;//小 if (!this.compareDates(date)){ x=this; y=date; }
2、题目集4,7-3 图形继承,在提交这道题的时候,数据输入非法时,系统输出Wrong Format,但是我刚开始写的代码中就会多输出前面初始化的时输出的语句,例如Constructing Shape Constructing Rectangle Constructing Box,查询原因后发现是new对象后再判断的输入是否合法,应该在这new之前就判断是否合法。
就像下面这样:
if (next<0){ System.out.println("Wrong Format"); System.exit(0); } Circle c=new Circle (next);
3、在进行关键词次数统计时,最后一个测试点总是无法通过,经过多次的检查确定当前思路无误后,通过查阅相关资料,发现要对拼接后的字符串组进行特殊处理才能通过。
给出部分源码:
public static void main(String[] args) { Scanner in=new Scanner(System.in); String []key= { "abstract","assert","boolean","break","byte","case","catch", "char","class","const","continue","default","do","double","else", "enum","extends","false","final","finally","float", "for","goto","if","implements","import","instanceof", "int","interface","long","native","new","null","package", "private","protected","public","return","short","static", "strictfp","super","switch","synchronized","this","throw", "throws","transient","true","try","void","volatile","while"}; String s; StringBuilder ss=new StringBuilder(); while(1>0){ s=in.nextLine(); if(s.equals("exit")) { break; } if(s.matches("(.*)//(.*)")) { String a[]=s.split("//"); ss.append(a[0]); } else { ss.append(s); } } if(ss.toString().equals("")) { System.out.println("Wrong Format"); System.exit(0); } String s1=ss.toString(); Pattern pattern=Pattern.compile("\"(.*?)\""); Matcher m=pattern.matcher(s1); while(m.find()) { s1=s1.replace(m.group()," "); pattern=Pattern.compile("\"(.*?)\""); m=pattern.matcher(s1); } pattern=Pattern.compile("/\\**(.*?)/"); m=pattern.matcher(s1); while(m.find()) { s1=s1.replace(m.group()," "); m=pattern.matcher(s1); } ArrayList<String> list=new ArrayList<String>(); s1=s1.replace("["," "); s1=s1.replace("]"," "); s1=s1.replace("-","Z"); s1=s1.replace("*","Z"); s1=s1.replace("/","Z"); s1=s1.replace("+","Z"); s1=s1.replace(">","Z"); s1=s1.replace("=","Z"); s1=s1.replace("!","Z"); s1=s1.replace(":","Z"); s1=s1.replace("\\","Z"); s1=s1.replace("&&","Z"); s1=s1.replace("||","Z"); s1= s1.replaceAll("[^a-zA-Z]", " "); String[] s2=s1.split("\\s+"); for(int i=0;i<s2.length;i++) { for(int j=0;j<key.length;j++) { if(s2[i].equals(key[j])) { list.add(key[j]); //count.add() } } } String t[]=new String[list.size()]; for(int j=0;j<list.size();j++) { t[j]=list.get(j); } String temp; for(int i=0;i<t.length-1;i++) { for(int j=0;j<t.length-1-i;j++) { if(t[j].compareTo(t[j+1])>0) { temp=t[j]; t[j]=t[j+1]; t[j+1]=temp; } } } int count[]=new int[t.length]; for(int j=0;j<t.length;j++) count[j]=0; for(int j=0;j<t.length;j++) { for(int i=0;i<t.length;i++) { if(t[j].equals(t[i])) count[j]++; } } for(int i=0;i<t.length;i++) { t[i]=count[i]+"\t"+t[i]; } ArrayList<String> list1=new ArrayList<String>(); for(int i=0;i<t.length;i++) { if(!list1.contains(t[i])) list1.add(t[i]); } for(int i=0;i<list1.size();i++) System.out.println(list1.get(i)); } static Set<Object> ifRepeat4(Object[] arr){ Set<Object> num000 = new LinkedHashSet<Object>(); for (int i = 0; i < arr.length; i++) { num000.add(arr[i]); } return num000; }
4、题目集5,7-5 日期问题,我在前面一次作业的基础上修改了部分代码,但是一直有个点过不去,答案错误,原来是输出的语句变了,吐了,,,,,
5、题目集6(7-5)图形继承与多态,这道题在计算三角形面积时,我选用的时海伦公式,但是在判断三角形的合法性的时候,我遇到了点问题,着急判断两边之和和两边之差,就想着先排好序再判断,但是却忘记了判断三边是否大于0,也是醉了。
部分源码如下:
public boolean validate() { if(getSide1()>0&&getSide2()>0&&getSide3()>0&&getSide1()+getSide2()>getSide3()&&getSide1()+
getSide3()>getSide2()&&getSide3()+getSide2()>getSide1()) return true; else return false; }
6、其实还有很多踩坑,但是时间有点久远,都记不清楚了。
四、改进建议
1、减少if else语句的使用,对于if-else结构过多导致的圈复杂度过高的问题,无非就是三目运算符,提前return,switch-case,表驱动法等优化方案,或者创建相应的类执行i并简化if-else的任务实现功能,最后则是改变自己的思路,探究有无更加优秀的算法或是解法了.
2、精简代码,代码复杂的自己都要看不懂了。
3、加强对list、group、字符串的一些操作、以及加强对一些概念的理解,类间关系应该如何去应用。
4、提高自己逻辑性和严谨性,每次的代码都会因为不够严谨而出错。
五、总结
1.通过这三次的PTA题集训练,使我对于JAVA语言面向对象进行编程设计的本质以及特点拥有了更加深刻的理解,在程序代码的编写过程中懂得了一个合理程序框架的构建对于程序以及编写者的影响之大,在今后的学习以及代码学习之中,我们都应保持良好的编程习惯,做到"三思而后行",使我们的工作质量大大提高并达到事半功倍的效果.
2.对于继承、多态、接口,封装等多种编程设计思路以及技术运用,由于使用的次数较少且理解程度并未到达透彻的境界.因此我们还需对其加强学习并多多进行训练,以提高自身的综合水平.
3.从目前的情况来看,老师的教学方法大体来说使没有问题的,代码实操以及案例详解的教学方式可以让同学们更好的理解对应知识点。
4.通过利用不同的题目对自己所掌握的知识进行考察,我发现自己仍存在对于知识点理解不够透彻以及不能灵活运用等问题,这就需要自觉进行查缺补漏了,相信随着时间推移,我能够取得相应的进步.
5.总体来说,相比于前几次的大作业,这次难度确有所上升,不过也切身让我们体会到了面向对象的封装继承多态的优势。

浙公网安备 33010602011771号