Blog作业总结02

前言:

第二阶段的oop作业与第一阶段的作业相比,其难度大大提高,知识点也更加贴合面向对象的使用。在本阶段,将会更加深入学习面向对象的思想,用更加合理的方式完成相应的功能。


 

题目集四:

题量:较少

难度、所含知识点:与前三次相比,此次的题目难度陡然上增,第一题的水文数据校验及处理来的让人错手不防。该题让我们开始了正则表达式对输入数据进行合法性校验及计算的学习之路。第二题的日期问题面向对象设计(聚合一)让我们了解到了聚合关系的使用方法,以及代码的有关书写。第三题的图形继承则是构造父类和子类,并使用this及super等关键字,使代码复用性提高。


题目集五:

题量:中等

难度、所含知识点:前三题都是结合排序的基础题型,难度并不大;第四题统计Java程序中关键词的出现次数要求使用Set和List或Map等接口;而第五题日期问题面向对象设计(聚合二)则是对上题目集日期类的改编版,使Year、Month、Day与DateUtil的聚合关系发生变化。


题目集六:

题量:中等

难度、所含知识点: 前四题都是对简单正则表达式训练,其难度并不大,其中,6-7-2的排序问题采用了将字符转义成ASCII的形式再进行排序,为字符的排序增添了一种新的方法;题五图形继承与多态要求掌握类的继承和多态,本次作业以抽象类定义实体类构建,而两个方法分别是方法重载和方法重写。并且使用了ArrayList类型的列表,对 list 中的图形对象在 list 中进行升序排序;题六实现图形接口及多态性涉及了实现类的封装性、继承性和多态性。

 


 

4-7-2:日期问题面向对象设计(聚合一)

1、设计与分析

(1)设计:

实验要求设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1900,2050] ,month∈[1,12] ,day∈[1,31]。分别实现求下n天、前n天、以及两个日期相差的天数。此题的所要实现的功能是结合了前一阶段的opp作业日期类题目进行的汇总。

 

首先用三个case选出一种功能进行实现。接着,分别将剩下四个类的框架以及其中所含有的变量写好。代码在主函数里通过DateUtil dateutil=new DateUtil(year,month,day)将年月日三个值传入DateUtil类中,并用

 

来接收三值;在此,同时创建Day类,同时将三值传入Day中,实现两类之间的连接,从而以此类推完成全部的连接。

 

在校验数据时,由于我们是先输入年的,且如果年出现问题,则compareDates(DateUtil date)方法直接返回false,程序则直接输出"Wrong Format"结束,故此我推荐的是先判断年再是月,接下来是日。

在求下n天和前n天时,结合前几次的作业思路发现,只要在所求方法之前加入while循环,同时结合equalTwoDates(DateUtil date)getDaysofDates(DateUtil date)方法进行两个日期之间的比较,代码如下。

public boolean equalTwoDates(DateUtil date) {//3 2014 2 14 2020 6 14
        // boolean equalTwoDates=false;
        return day.getMonth().getYear().getValue()==date.day.getMonth().getYear().getValue()&&day.getMonth().getValue()==date.day.getMonth().getValue()&&day.getValue()==date.day.getValue();
        
    }
    public int getDaysofDates(DateUtil date) {
        
        int b=1;
        if(this.compareDates(date)==true) {
        while(!this.getNextNDays(1).equalTwoDates(date)) {
            b++;
        }
        }
        else{
            while(!this.getPreviousNDays(1).equalTwoDates(date)) {
                b++;
            }
        }
        return b-1;
    }

 

如果两日期相等,则循环结束。与前面不同的时,在计算前n天时,只需要考虑1月和3月两个特殊月份,其余可一概而论。

同时由于2月的特殊性,这里在运用resetMax()方法时要注意修改数组中的日期28或29。

(2)分析:

根据PowerDesigner的类图显示,该题采用的是聚合的关系。根据题目所给类图可知,Year聚合Month,Month聚合Day,Day聚合DateUtil类。该四个类就像贪吃蛇一样一节节的相连,每个环节都不能出现问题。

 

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:

 

 

2、踩坑心得

(1)首先是上面提到的校验数据时,由于我一开始没有在意三者是否合法的顺序,导致程序结果和逻辑出现一些问题,后面经过顺序的调整,通过测试点。

(2)同时需要注意的是,在让年月日进行加减时,最好也是采用从年到日的顺序进行计算。

(3)由于一开始对这种聚合关系的掌握还不是特别深刻,在不同类中获取相应年月日的值成了问题所在,但是通过查找资料学习了解之后,学会了用day.getMonth().getYear().getValue()、(day.getMonth().getValue()和day.getValue()得到三者的值。此类有关问题便迎刃而解。

3、改进建议:

为防止代码超出指定范围,可通过数据比较,一次减去一整年的天数再是一个月的天数,最后再是一天天的加减计算。只是与上次相比,方法不同。

 

 

5-7-5:日期问题面向对象设计(聚合二)

1、设计与分析

(1)设计:

实验要求设计如下几个类:DateUtil、Year、Month、Day,其中年、月、日的取值范围依然为:year∈[1820,2020] ,month∈[1,12] ,day∈[1,31] 。本题所要实现的功能及方法和4-7-2中的几乎一致,只不过使用的是Year、Month、Day分别与DateUtil形成的聚合关系。

同时,由于年月日三个类是独立出来的,故此在DateUtil中获取三者的值直接采用year.getValue()、month.getValue()和day.getValue()即可。

结合上日期类聚合的心得,在此我采用了通过整加或减年数来减少代码的运行时间,部分代码框架如下。

 

        long i=0;
        
        while(i < n) {}
while(n - i >= 366)while(i < n) {
            i++;
        }
        
        return this;
    

(2)分析:

根据PowerDesigner的类图显示,该题采用的是聚合的关系。根据题目所给类图可知,Year、Month、Day分别聚合DateUtil类。该四个类就像树枝一样分开的

 

 

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:

 

 

由图可知,由于该题DateUtil中的getNextNDays(long n)if语句过多,导致复杂度极高。

2、踩坑心得

(1)本题中值得注意的是,如下图的两个测试点较难通过,可能的原因是因为代码中采用一天一天的减法会使代码结果运行过慢,导致运行超时。故此,在本题最好采用整年或整月循环的方式求解想日期相差的天数及前或下n天。

 

 

(2)同时,由于本题特殊的测试点以及逻辑关系,在计算下或前n天时,应当先进行判断是否加或减到两日期相同,再进行年月日三者值的改变。

3、改进建议:

 我在此题仅是做了整年循环的处理,但如果时间充沛,最精简的方式是再加上整月的处理。

优劣比较

 题目集4(7-2)、题目集5(7-4)两种日期类聚合设计

运行效率:经过两次测试的截图可知,第二种日期类前n天整数型最大值测试点的运行时间相比第一种要长出很多(在此也是由于多个嵌套if增加了复杂度),说明第二种的代码运行效率较低。

 

 

 

 

聚合关系:由于两种不同的聚合关系可知,第一种的关系是一环套一环,当前面的类出现问题时,后面类的结果也会受其影响,而第二种则是许多环套在同一个大环中,其中一个出现问题,另外的类是不会出现问题的,问题排查性也能大大提高。

 

4-7-3:图形继承

1、设计与分析

(1)设计思路:

实验要求设计如下:

 

 

由此可知首先通过switch进行功能的选择,用double radius=input.nextDouble();circle.setRadius(radius);实现数据的输入。

其次,写出父类Shape,其次,用Circle和Rectangle进行父类的继承(class Circle extends Shape)、(class Rectangle extends Shape)接着用Ball和Box分别继承Circle(class Ball extends Circle)和Rectangle(class Box extends Rectangle),以上是整体继承的大致结构。

接下来分别实现各个类中的构造方法(此类问题具体所需注意的已写在注释里)

void circle (){
         System.out.println("Constructing Circle");
    }
void rectangle() {
           System.out.println("Constructing Rectangle");
    }
double ball() {
           System.out.println("Constructing Ball");
           double ballarea=super.getArea()*4;//这里super.父类(extends后面的类)可直接利用该方法中的数据
            System.out.printf("Ball's surface area:%.2f\n",ballarea);
           return ballarea;
    }
double box(double height) { 
           System.out.println("Constructing Box");
           double boxarea=((height*super.getLenth())*2)+((super.getWidth()*height)*2)+super.getArea()*2;
            System.out.printf("Box's surface area:%.2f\n",boxarea);
           return boxarea;
    }

 

最后分别添加重写父类继承来的求面积方法

 

public double getArea() {
        System.out.println("Constructing Shape");
                  return 0.0;//求图形面积
    }
@Override//重写父类
     public double getArea() {//(只要方法不是void而是get加什么什么(首字母大写)一定要有return值,而这里的return值是用来get获取数据的:见circle.getArea()第25行代码)
        double calrad=0;
        calrad=Math.PI*radius;//以后圆周率不能用3.14代替
        return calrad;
    }
@Override//重写父类
     public double getArea() {
        double calrect=width*lenth;
      return calrect;          
    }

(2)分析:

根据PowerDesigner的类图能明显的看出上述所说的继承关系,同时在Circle和Rectangle中都有Shape中getArea的重写方法。

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:
 

 

 

由图可知,此题复杂度略大。主要体现在switch下的if语句中,而其他类的方法中都是只有1的复杂度。

2、踩坑心得

(1)本题中值得注意的是,在算圆的面积的时候,用3.14来代替Π测试点是无法通过的,换成Math.PI后,测试点通过。

(2)由于第一次做父类继承的题,一开始在方法重写时,不知如何使用,后经过学习了解了 superthis的区别和用法

(3)由于double box(double height)中的height由于不是私有属性,在主类中定义后就不用再定义(否则这里的height仍为0),要用时可直接box.box(height);将height传进来。

  (4)同时,在输入进入case3的时候,radius无需再定义否则报错double radius=input.nextDouble();因为这里用到的是已经在circle中定义了的radius。

3、改进建议:

目前想到的改进便是将数据直接通过输入传入set方法中,例:circle.setRadius(input.nextDouble());

 

6-7-5: 图形继承与多态

1、设计与分析

(1)设计思路:

  1. 实验要求设计如下:从键盘首先输入三个整型值(例如a b c),分别代表想要创建的Circle、Rectangle及Triangle对象的数量,然后根据图形数量继续输入各对象的属性值(均为实型数)进而实现以下四个功能(必须使用面向对象的封装性、继承性及多态性以及ArrayList)
  • 各个图形的面积;
  • 所有图形的面积总和;
  • 排序后的各个图形面积;
  • 再次所有图形的面积总和。

首先根据输入要求创建不同数量的Circle、Rectangle及Triangle对象,这里采用for循环的方法进行。同时,想到最终的所有数据(shape)要存入ArrayList数组中进行判断和处理ArrayList<Shape> list = new ArrayList<>();,故此我在这重新写了一个类All来装ArrayList、判断数据合法性、计算数据和排序的方法,并在主函数中直接利用for循环,用add方法把所要创建的对象存入动态数组中。

for(int i=0;i<a;i++) {
                all.add(new Circle(input.nextDouble()));
            }
            for(int j=0;j<b;j++) {
                all.add(new Rectangle(input.nextDouble(),input.nextDouble()));
            }
            for(int k=0;k<c;k++) {
                all.add(new ,Triangle(input.nextDouble(),input.nextDouble(),input.nextDouble()));
            }

 

本次作业采用的是抽象类定义、实体类构建的方式,且根据题目所示,Shape为抽象类,getArea、validate、toString均为抽象方法,故此均要在前面加上abstract

同时此题仍有重写方法校验数据合法性和保留小数的操作方法

@Override
    public boolean validate() {
        boolean validate=false;
        if(radius>0) {
             validate=true;
             getArea();
            }
            else {
               validate=false;
            }
            return validate;
    }

    @Override
    public String toString() {
        return String.format("%.2f",this.getArea());
        
    }

这里涉及到知识点是任意符合条件的的三条边(两边之和大于第三边【这里已采用插入排序的方法进行了第一遍的筛选,合法性的判断更加简单】)求其面积:

double num=(side1+side2+side3)/2;
        return Math.sqrt(num*(num-side1)*(num-side2)*(num-side3));

在计算三个对象的面积之和时,又要用到动态数组中的get方法进行计算

for(int i=0;i<list.size();i++) {
            allarea=allarea+list.get(i).getArea();
        }

输出排序后各面积的输出我在这也使用了插入排序的方法进行,将排完序的数据用for循环重新存入动态数组中进行输出list.set(j, list.get(j-1));

(2)分析:

根据PowerDesigner的类图能看出明显的继承和重写方法。

 

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:

 

 

由图可知,此题复杂度相比上一题较小。主要由于if语句多少分布在一些合法性的判断上。

2、踩坑心得

(1)本题在刚开始创建对象时就出现了问题。一开始我并没有将创建的对象添加至动态数组中,导致list.size()一直是0的状态

(2)同时由于第一次运用ArrayList进行数据的获取和传入,有关知识点还不熟,于是通过查阅书上的有关材料以及菜鸟教程里面的有关信息了解到了相关用法list.get(i).getArea()list.set(j, list.get(j-1));

(3)这里在进行数据排序时,一开始是想用冒泡排序,但是由于书写不当,有些测试点总是过不了,最后换成插入排序,测试点一次性通过。

(4)同时注意,这里第一次接触抽象类和抽象方法,通过学习才知道要使用abstract进行抽象表达。

(5)有一个0的测试点需要注意的是,当t[0]>=0&&t[1]>=0&&t[2]>=0时,要返回true才能通过该测试点。

3、改进建议:其实这里在判断三角形三边合法性时,如果想让代码看起来较为简便,可以不用数组和插入排序对其边进行排序,而是直接在if语句中加入三个两边之和大于第三边便是。

 

6-7-6: 图形继承与多态

1、设计与分析

(1)设计思路:

实验要求设计如下:

  • GetArea为一个接口,无属性,只有一个GetArea(求面积)的抽象方法;
  • Circle及Rectangle分别为圆类及矩形类,分别实现GetArea接口
  • 要求:在Main类的主方法中分别定义一个圆类对象及矩形类对象(其属性值由键盘输入),使用接口的引用分别调用圆类对象及矩形类对象的求面积的方法,直接输出两个图形的面积值。(要求只保留两位小数)

首先本题第一次引入了接口的概念,且接口中有一个抽象方法,通过查阅相关资料后了解了接口的大致用法

interface GetArea{
    public static  double getArea() {
        return 0.00;
    }
}

同时要求使用到类的封装性、继承性和多态性,故此在Circle及Rectangle类中分别创建get与set方法,同时重写getArea方法(用接口的引用分别调用圆类对象及矩形类对象的求面积的方法)。

(2)分析:

根据PowerDesigner的类图能明显的看出Circle及Rectangle类继承接口GetArea,同时调用了接口中getArea的方法

 

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:

 

 

由图可知,此题复杂度由于代码量本身偏小,且只有一个if else语句,故复杂度很小。

2、踩坑心得

(1)本题第一次接触接口,导致刚开始写时连基本的书写格式都不清楚,但通过书上的知识学习后,了解了interfaceimplements GetArea的用法后,问题便迎刃而解

正则表达式技术的分析总结

 

正则表达式训练-QQ号校验

  • 要求必须是 5-15 位;
  • 0 不能开头;
  • 必须都是数字;
Pattern  p1 = Pattern.compile("^(?!0)[0-9]{5,15}$");

^和$分别表示开头和结尾的标志;

(?!+数字)表示不能以该数字为开头;

[0-9]表示全部为数字;

{数字,数字}表示位数(若为{0,}则表示无限数字);

 

正则表达式训练-验证码校验

要求:验证码是由四位数字或者字母(包含大小写)组成的字符串。

Pattern  p1 = Pattern.compile("^[a-zA-Z0-9]{4}$");

这里[a-ZA-Z0-9]表示全体大小写字母或数字都能匹配

 

正则表达式训练-学号校验

对软件学院2020级同学学号进行校验,学号共八位,规则如下:

  • 1、2位:入学年份后两位,例如20年
  • 3、4位:学院代码,软件学院代码为20
  • 5位:方向代码,例如1为软件工程,7为物联网
  • 6位:班级序号
  • 7、8位:学号(序号)

要求如下:

  • 只针对2020级
  • 其中软件工程专业班级分别为:202011~17、61,物联网工程专业班级为202071~202073,数据科学与大数据专业班级为202081~82
  • 每个班级学号后两位为01~40
Pattern  p2 = Pattern.compile("^[0-9]{8}$");
Pattern p1 = Pattern.compile("(2020)(1[1-7]|7[1-3]|8[1-2])([0-3][1-9]|40)");

在正则中用()进行匹配区间的划分提取匹配的字符串,用“|”表示或;

(a[b-c])表示以数字a为十位数,以b到c之间包括本身的数字为个位数;

 

 统计Java程序中关键词的出现次数

要求匹配指示符和符号以及关键词

       str=sb.toString().replaceAll("\\/\\/.*"," ");//匹配//
        str=str.replaceAll("/\\*{1,2}[\\s\\S]*?\\*/"," ");//匹配/*—*/
        str=str.replaceAll("\"([^\"]*)\""," ");//匹配“”
        str=str.replaceAll("\\p{Punct}"," ");//匹配部分符号
        Pattern  p1 = Pattern.compile("\\b"+impor[i]+"\\b");//匹配单独出现的字符串,而不是只要有该符合的字符就能进行比对

本题在匹配完之后用空格进行代替,从而达到删去的目的。

 

水文数据校验及处理

假定分水口门的数据上报时是采用人工输入的方式,每一行代表一个整点时刻的分水数据,各数据之间采用“|”符号进行分隔,每次可以输入多条数据,直到遇到用户输入“exit”为止,每一行输入数据共包含五部分:测量时间、目标水位、实际水位、开度(包含目标开度和实际开度,以“/”分隔)、流量。 各数据格式要求如下:

  1. 测量时间:格式为“年/月/日 时:分”,其中年份取值范围为[1,9999],“月”与“日”为一位数时之前不加“0”,日期与时间之间有一个空格,“时”与“分”之间采用冒号分隔(英文半角),“时”为一位数时之前不加“0”,“分”始终保持两位,且始终为“00”。注意:“时”数必须是24小时进制中的偶数值。
  2. 目标水位、实际水位、流量:均为实型数,取值范围为[1,1000), 小数点后保留1-3位小数或无小数(也无小数点)
  3. 目标开度、实际开度:实型数,取值范围为[1,10),必须保留2位小数,两个开度之间用“/”分隔
  4. Pattern  p1 = Pattern.compile("((?<!\\d)[1-9][0-9]{0,3})/([0-9]|[1][0-2])/(([1-2][0-9]|[3][01])|[1-9]) (([02468])|([1][02468])|([2][024])):([0][0])");
    //匹配时间:年/月/日 时:分
    //\\d匹配数字\\w匹配字母
    //(?<!a)b:查找前面不是 a 的 b
    Pattern  p2 = Pattern.compile("((?<!\\.)[1-9][0-9]{0,3}\\.[0-9]{1,3}(?!\\w))|(?<!(\\.|\\d))([1-9][0-9]{0,3})(?!((\\.)|\\d))");
    //匹配水位、流量
    Pattern  p3 = Pattern.compile("(([1][0]\\.[0][0])|(?<!\\d)([\\d]\\.[\\d][\\d]))");
    //匹配开度  

     

5-7-4:统计Java程序中关键词的出现次数

1、设计与分析

(1)设计思路:

实验要求设计如下:编写程序统计一个输入的Java源码中关键字(区分大小写)出现的次数。

  • 注释中出现的关键字不用统计
  • 字符串中出现的关键字不用统计
  • 统计出的关键字及数量按照关键字升序进行排序输出
  • 未输入源码则认为输入非法

输入Java源码字符串,可以一行或多行,以exit行作为结束标志且必须使用List、Set或Map中一种或多种

首先将百度搜索的53个关键词放入一个字符串数组中,接着实现“以exit行作为结束标志”的要求,这里最好使用StringBuilder输入较多的字符串,并用append的方法将每行字符串相连,代码如下:

StringBuilder sb = new StringBuilder();
        String word=input.nextLine();
        while(!word.equals("exit")) {
        sb.append(word);
        sb.append("\n");
        word=input.nextLine();
        }

同时直接使用list接口,将输入的一段代码放入动态数组中,进行接下来的操作

List<String> list=new ArrayList<String>();
        list.add(sb.toString());

如果获取到的字符串长度为0则直接输出非法输入,否则进入正则的分割和筛选

if(list.get(m).length()==0) {
                    System.out.println("Wrong Format");
                }

在这里,我采用的思想是用正则表达式进行匹配,如果匹配到所要跳过的字符,则直接用空格进行代替

 

 

紧接着将53个关键字进行升序排列后,再一次用Pattern  p1 = Pattern.compile()和Matcher  m1 = p1.matcher()方法将关键词和空格加字符串进行匹配,如果找到,则m1.find()返回true并利用j++进行计数操作,最后按照格式输出数据。

(2)分析:

根据PowerDesigner的类图可知本题只有一个类

 

 

使用SourceMonitor对该函数的复杂度进行检测,结果如下:

SourceMonitor的生成报表:

 

 

由图可知,此题复杂度由于代码量本身偏小,且只有两个if else语句,故复杂度很小。

2、踩坑心得

(1)该题在一开始由于想的太多,且受上面水文的影响,首先想的是先将输入的源代码按行分割再存入数组中,再每行通过符号用空格代替进行分割开,但是后来由于代码较为复杂,故想到了直接用空格代替全部的符号,用关键字数进行循环,每次在str中找到相关关键字就进行计数并在最后输出。

(2)同时,一开始我会担忧,如果进行匹配,如果识别到别的关键字,则计数符号j的值会重新改变,但在查资料后发现正则的比对是将一个字符串比对到底,直至再也无法发现相同字符串后,才会进行下一个数据的比对。

(3)特别注意的是,在本题会容易忽略一个问题

例:nice

输入:khlkjnicehlkh

      nice

这会匹配到两次nice

所以要用\\b+nice+\\b排除这种情况

 

3、改进建议:

在用正则进行符号代替时,最好不要使用\\p{Punct},该正则可能会忽略一些符号,最好还是推荐采用str=str.replace("[", " ");的格式进行书写,不然无法通过本题的测试点。

 

作业总结:

1、本阶段的作业相比上一阶段难度加大了许多,但在此阶段我们也学习收获了更多更贴合面向对象的知识。例如这一阶段的难点——正则表达式,开始所认为正则只是为了进行匹配从而判断true或false,但写到后面的题时发现,我们可以通过匹配进行有需要性的替换某些数据,达到删除和替换功能的实现。

2、其次,面向对象单靠一个main类是完全不够的,在这一阶段,多个类分别完成不同的方法,同时将其组合起来,形成完整的功能体系,这本阶段,类的封装、继承、多态性体现的淋漓尽致。经过多次联系,我们已清楚的了解到运用类的继承和多态其中的方法和一些代码的书写方式,这可以大大提高代码的复用性,使代码量大大减少,同时方法的重写也更加符合现实中的逻辑思维。同时,接口的使用也再次拓宽了我们的知识面,了解了如何利用Map、List、Set等接口进行一些数据处理的优化。

3、总的来说,虽然难度上升的比较突然,但是这又同时刺激提高了我们在短时间内学习不同知识点的能力。在后两次题目集中,难度也是慢慢上升,且主要的难题都是对前一阶段的升级版,在前面的思想上,采用更加精确的思维模式来完成几乎同样的功能。

 

                                                                                                                                                                                                                                                                                                                    2021-05-02

posted @ 2021-05-02 22:43  张艺兴的小仙女  阅读(147)  评论(1)    收藏  举报