OO第二次博客作业

一、前言

  这三次作业总体来说难度中等偏上,其中难点主要集中在正则表达式的运用、类的继承及类与类之间聚合上。题量每次逐渐增多,但相对之下难度有些许降低,总体来说题量适中。这三次作业的知识点主要集中在类的运用上,主要为类的继承、多态、抽象以及类的封装上。通过设计类对时间及日期的处理,让我逐渐了解了类与类之间的关系,以及每次修改部分题目要求及类图,让我更能循序渐进地去理解合理设计类的重要性,以及懂得了类之间地耦合性的重要性。另一大知识点则是正则表达式的运用,这一块知识点上课并没有讲解,要求我们通过自学的方式掌握并理解。正则表达式的语法相对并不是很难,但学好并不容易,在这三次的题目中,就有许多地方有很多的小细节需要特别注意并处理。通过完成这三次题目,也让我学到了很多知识。

二、设计与分析

  ①题目集4(7-2)、题目集5(7-5)两种日期类聚合设计的优劣比较:

  这两道题的知识点主要都是考察类的聚合。但两道题的聚合设计并不相同,也有优劣之分。

   7-2类图:  

  

 

   通过类图可以看到,这种日期类的设计主要是层层递进的关系。DateUtil为总的日期类,其中包含了Day类(存储天数相关信息及方法),而Day类中又包含了Month类(存储月份相关信息及方法),Month类中则包含了Year类(存储年份相关信息及方法)。这种层层包含的设计显而易见有个不合理之处,Day、Month、Year三个类之间的关系不应该是这种一个包含一个的关系,这三个类应该是平等的。因此,在调用相关方法的时候,总是会很麻烦。例如我要调用Year类中的方法,就还要通过Month类进行调用,增加了很多不必要的麻烦,这是很不合理的设计。而相比之下,7-5的设计就好了很多。

   7-5类图:

   从中对比看以看出,种设计让Day、Month、Year三类全部包含于DateUtil类,在需要调用相关类的方法时,只要通过DateUtil这个类进行调用,方便了不少。并且也可以看出这样的设计也更符合实际逻辑,使Day、Month、Year三类的关系更加完善,而不是像上一种设计那样毫无逻辑。

   程序复杂度图:

   

   这两道题目总体上难度一般,主要是理解类的设计的重要性。但其中应该要注重小细节,我也曾经因为一些小细节漏判了一些特殊情况,导致程序出现bug。

   ②题目集4(7-3)、题目集6(7-5、7-6)三种渐进式图形继承设计的思路与技术运用(封装、继承、多态、接口等)

    第一道题主要是实现类的继承并根据子类重写不同的方法。

    7-3类图:

    

    从类图中可以看到父类为Shape类其中只有构造方法和一个求图形面积的方法。其有两个子类Circle类和Rectangle类并在这两个类中根据对应类的实际情况重写了求面积方法。可以看出Circle类和Rectangle类都有些许相同之处,但也有不同之处。通过继承同一个父类,使它们的关系更加密切与实际。而Circle类和Rectangle类都各自有一个子类,分别是Ball类和Box类。从实际上来看,CIrcle和Ball之间也有很多的相似之处,Rectangle类与Box类之间也是如此。Ball类和Box类更像是它们父类的一个扩展,从二维到三维。

    7-5类图:

    

    复杂度图:

    

    这道题目相比之前增加了一个要求,需要求所有图形的面积及面积之和,并根据面积大小排序。在这次对类的设计上,将Shape修改为抽象类,使设计更加合理。通过重写父类的方法,可以增加多态性,消除了类之间的耦合关系,并且增加了可扩充性及灵活性等。也让我了解了多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。在对面积排序方面上,我通过重载比较器,再调用Collections类的排序方法进行排序。

    7-6类图:

    

    复杂度图:

    

    与前两道题不同,在这道题中,新增了创建接口的要求,而不再创建父类,通过继承父类进而重写方法。从中也可以看出,运用接口可以增加更多的灵活性。从中也可以看出接口与抽象类既有相同之处,也有不同之处。一个接口可以和类一样有多个方法,但接口没有构造方法,也不能实例化对象,而且接口中的方法必须都是抽象的方法,接口还支持多继承。

    可以看到复杂度降低了一些。

    ③对三次题目集中用到的正则表达式技术的分析总结

     在这三次作业中都有正则表达式的相关题目。正则表达式常常用来检测一些文本或字符串,可以说是极其重要的。在学习正则表达式的过程中也遇到了许多的问题,例如起初 '\' 字符总是和平常一样,只打一个代表转义,但后来查阅了相关资料才知道两个 \\ 代表其他其他语言中的一个 \ ,而表示一个普通的反斜杠字符则是 \\\\ 。在学习中发现,有许多文本字符有许多不同的特点,需要去加以判断,不然很有可能会出现bug。通过这几次正则表达式相关的作业学习,我也发现正则表达式的重要性,以及它的功能的强大。

     例如题目集6中的7-4,要求检验一个学号是否合法,通过正则表达式,可以轻松地将字符串分割、替换、再进而检验其合法性。

     源代码:

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String st = sc.nextLine();
        Pattern p = Pattern.compile("\\D");
        Matcher m = p.matcher(st);
        if(m.find() || st.length() != 8) {
            System.out.println("错误");
            System.exit(0);
        }
        int year = Integer.valueOf(st.substring(0, 4));
        int cla = Integer.valueOf(st.substring(4, 6));
        int id = Integer.valueOf(st.substring(6));
        if(year != 2020 || !((cla >= 11 && cla <= 17) || cla == 61 || (cla >= 71 && cla <= 73) || cla == 82 || cla == 83) || id < 1 || id > 40)
            System.out.println("错误");
        else
            System.out.println("正确");
    }

}

     而7-1则是要求检验一个QQ号是否合法。其主要判断合法性就是检测长度及是否为全数字。而判断是否有除数字外其他的字符,通过正则表达式可以轻松解决。

     源代码:

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String st = sc.nextLine();
        Pattern p = Pattern.compile("\\D");
        Matcher m1 = p.matcher(st);
        p = Pattern.compile("^0");
        Matcher m2 = p.matcher(st);
        if(st.length() < 5 || st.length() > 15 || m1.find() || m2.find())
            System.out.println("你输入的QQ号验证失败");
        else
            System.out.println("你输入的QQ号验证成功");
    }

}

     而相对复杂的题目例如题目集4的7-1,要求我们对水文数据校验及处理,需要统计大量的字符串文本。但使用正则表达式则可以方便许多。通过正则表达式将每种对应的数据分割再进而检验合法性。由于这道题难度及复杂程度高了许多,因此设计了相关类,模块化了程序。

     类图:

  

     ④题目集5(7-4)中Java集合框架应用的分析总结

      在这道题中不光有正则表达式的知识点,还有一些Java的集合框架,例如HashMap,HashSet、List等这些框架可以方便地存储一些变量,而且还有一些特别的用处。List有些像常规的数组,但又比常规地数组高级了不少,提供了相关的添加、删除、修改、遍历等功能,并且没有大小限制。并且可以存储泛型数据类型。而且可以通过Collections类对容器内元素进行排序,通过重载比较器还可以自定义排序规则,功能十分强大。而HashMap是一个散列表,它存储的内容是键值对(key-value)映射。它会根据键的 HashCode 值存储数据,具有很快的访问速度。最重要的是键可以不是数字,而是字符串或其他数据类型。它也具有添加、删除、修改、遍历等功能,并且有许多功能强大地方法以供调用。而HashMap则是基于HashMap的,HashMap的元素实际上是对象。可以理解为一个集合,其中不允许相同的元素存在,集合中的每个元素都必须是唯一的。因此可以利用HashMap来进行简单的判重,很是方便。而在这道题中可以利用HashSet判断是否出现过某个关键字,再利用HashMap记录对应关键字的出现次数。最后将对应的关键字存储在List中进行排序,最后迭代输出。

      源代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

    static String[] jug = {"public", "protected", "private",
            "class", "interface", "abstract", "implements", 
            "extends", "new", "import", "package", "byte", 
            "char", "boolean", "short", "int", "float", "long", 
            "double", "void", "null", "true", "false", 
            "if", "else", "while", "for", "switch", "case", 
            "default", "do", "break", "continue", "return", 
            "instanceof", "static", "final", "super", "this", 
            "native", "strictfp", "synchronized", "transient", 
            "volatile", "catch", "try", "finally", "throw", "throws", 
            "enum", "assert", "goto", "const"};
    static ArrayList<String> arr = new ArrayList<String>();
    static HashSet<String> set = new HashSet<String>();
    static HashMap<String, Integer> map = new HashMap<String, Integer>();
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String st;
        st = sc.nextLine();
        if(st.equals("exit")) {
            System.out.println("Wrong Format");
            System.exit(0);
        }
        while(!st.equals("exit"))
        {
            if(st.matches("(.*)//(.*)")) {
                String[] t = st.split("//");
                st = t[0];
            }
            arr.add(st);
            st = sc.nextLine();
        }
        
        st = " ";
        for(String elem : arr)
            st += elem + " ";
        
        Pattern p = Pattern.compile("\"(.*?)\"");
        Matcher m = p.matcher(st);
        while(m.find()) {
            st = st.replace(m.group(), " ");
            m = p.matcher(st);
        }
        
        p = Pattern.compile("/\\**(.*?)/");
        m = p.matcher(st);
        while(m.find()) {
            st = st.replace(m.group(), " ");
            m = p.matcher(st);
        }
        
        st = st.replace("[", " ");
        st = st.replace("]", " ");
        st = st.replace("+", "xx");
        st = st.replace("-", "xx");
        st = st.replace("*", "xx");
        st = st.replace("/", "xx");
        st = st.replace("<", "xx");
        st = st.replace("=", "xx");
        st = st.replace(">", "xx");
        st = st.replace("!", "xx");
        st = st.replace(":", "xx");
        st = st.replace("\\", "xx");
        
        p = Pattern.compile("[^A-Za-z]");
        m = p.matcher(st);
        st = m.replaceAll(" ");
        st += " ";
        
        for(int i = 0; i < jug.length; i++) {
            p = Pattern.compile("\\s+" + jug[i] + "\\s+");
            m = p.matcher(st);
            while(m.find()) {
                if(!map.containsKey(jug[i]))
                    map.put(jug[i], 1);
                else
                    map.replace(jug[i], map.get(jug[i]) + 1);
                set.add(jug[i]);
            }
        }
        
        ArrayList<String> res = new ArrayList<String>();
        for(String elem : set)
            res.add(elem);
        Collections.sort(res);
        for(String elem : res) {
            System.out.println(map.get(elem) + "\t" + elem);
        }
    }

}

三、采坑心得

    在提交过程中出现的主要的问题在正则表达式的相关题目上。例如题目集5的7-4,在其中提取单个单词后,直接将单词进行合并,结果是显然的,总是输出空,后来经过调试才发现所有单词都被合成了一个字符串。因此解决办法就是在合并的时候加空格分隔,这样就可以避免这个问题了。但是又出现了新的问题,检测出的关键字数不对。经过调试后发现有些关键字包含在某个单词中,但也被误判为关键字了,解决的办法就是在用正则表达式匹配的时候,在开头和结尾都加上空格符,这样必须是一个单词才会被匹配。

四、改进建议

     题目集4的7-1可以合理设计类用来存储相关的数据,并且在合适的类中添加对应的检验合法性的方法。也可以添加某个类专门用来提取和处理输入的文本并保存至对应的类中,还可以使用一些Java框架,例如List用来存储相关的数据。

     题目集6的7-5中要求按照面积对图形进行排序,一个显然的做法就是将面积提取至数组中,在进行排序。但更好的做法是重载比较器,再调用Collections类的排序方法,可以方便不少。

        Collections.sort(arr, new Comparator<Shape>() {
            @Override
            public int compare(Shape s1, Shape s2) {
                if(s1.getArea() < s2.getArea())
                    return -1;
                else if(s1.getArea() > s2.getArea())
                    return 1;
                return 0;
            }
        });

 

五、总结

     通过这个月的学习,加深了我对面向对象的理解,并学会了类的设计的重要性,以及类的封装、继承、多态以及接口的功能和运用。并且知晓了如何利用封装、继承、多态去优化程序,使之更具复用性,减少耦合度。而正则表达式方面则是加深了对细节的把握和处理,能够运用正则表达式处理相对较复杂的文本,并能够通过正则表达式检验其合法性。当然还有许多的不足,许多程序的类的设计还是不够合理,以及变量命名的方法不够规范。在对面向对象方面中类的封装、继承、多态还要进一步的研究,并加深理解。在课程改进方面觉得目前的教学方式良好,并无太多的建议。

 

posted @ 2021-04-26 18:29  qweqqewe  阅读(102)  评论(0)    收藏  举报