BLOG - Three

2022/6/18 23:23:29

本文主要是介绍BLOG - Three,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

  • 前言

   1.期中考试前言

   期中考试一共三道题,层层递进,每一题都对前一题进行改进,进行更深层面的运用面对对象语言去编程,同时引导我们一步步的去认识去熟悉面对对象编程的魅力,考察到了类的设计、类的继承、多态的使用、抽象类的设计、抽象方法的定义等等。第一题较为基础的设计了一个对于点、线之间的操作的编程实现,要求类成员都为私有类型。

  第二题在第一题的基础上,要求利用继承去解决点、线问题,并要求加入一个面的类型,在使用继承类的时候要进行多态操作,使用到向上造型,设计好点类、线类、面类之后只要按照题目要求去设计一些解决基本数学问题的函数就可以,题目整体和第一题相差不大。

  第三题就要求加入了容器类 ArrayList ,并且将继承和多态的操作融入其中,需要对 ArrayList 操作熟悉,例如ArrayList的定义,往里添加元素和删除元素,以及对ArrayList得到遍历等等,对继承和多态都要有一定的理解,也要求使用抽象类,抽象类不可用于声明对象,熟悉抽象类的运用问题也不大,同时第三题难度最大的部分就是容器类和继承类的结合使用问题,需要按照输入的序号来指定的定义点、线、面类,并执行其操作,且要删除ArrayList种指定位置的元素。

 

   2.PTA大作业六前言

  第一题是电信计费系列1,主要是对于座机用户进行设计,座机用户接电话不需要收费,则只需考虑拨打到南昌市内、江西省内、国内,这三种情况,分别将三种情况的通话记录存入对应的容器中,再去计算即可,总体在代码实现上,难度较大,代码也较长,设计起来较为复杂,还需要使用到容器的排序,要求对电话号码按字母序排序后输出。第二题是考察多态测试,难度较低,只需要掌握接口的基本使用即可设计完成,还考察了是否理解引用变量与对象实例之间的关系与区别、方法调用时引用类型参数的传递过程、private、protected、public等关键的使用场合与使用方法、对象组合的方式与方法、java中方法重载的实现方式。还需要去进行数据封装,在使用到继承类的时候,如何运用super也是一个关键点。

 

   3.PTA大作业七前言

  第一题是电信计费系列2,加入了手机用户,这就需要考虑到手机——手机通话、手机——座机通话、座机——手机通话、座机——座机通话,四种情况,也相应的需要修改正则表达式来进行输入格式的判断。难度最高,四种情况的细节都需要好好斟酌,座机接电话不收费,手机在省外接电话才需要收费,这些都需要考虑,还考察到了SimpleDateFormat类、正则表达式、继承、多态、抽象类,题量都较大,其中所包含的类较多,有的类作为父类或者抽象类,有的类作为子类,每个类负责不同的功能。第二题主要是一个通过重写函数,实现对容器内进行对应需求的排序问题,虽然题目有题解,但是除了实现Comparable接口之外,还有一种方法是实现Comparator接口,相对而言重写部分更少,第三题是阅读程序解决问题,难度不大,根据题目要求去写就好,使用到了一下第二题已经解决的问题方法。

 

   4.PTA大作业八前言

     第一题仍然是电信计费系列,电信计费系列3只需要考虑短信问题,由于座机无法发送短信,因此只有手机用户,相对于前两次电信计费的题目而言,这次题目应该是难度最低的,比较复杂的问题在于如何去计算短信的条数,按照题目要求短信内容是包括数字和一些符号的,这就需要好好的去考虑其中的正则表达式的设计,第二题是实现一个商店类,里面只是考察了一些基础的类与类之间的关系及操作问题,难度较低,解决起来也比较容易。第三题主要是考察了一下多态的内容,需要使用到向上造型,去把子类对象实例当作父类去使用,这些问题也不是第一次遇见了,之前就解决过多次,也是比较容易。

 

    这几次练习的难度差不多,从普通编程再到面对对象入门,考察的知识面广,还有对于类的设计,使我更加深入的了解到类的使用方法,理解面对对象的含义,包括对于集合框架的理解与使用,对于集合框架函数的重写,排序等。

 

  • 设计与分析

 

  复制代码

  一.期中考试第一题

  • 第一题简单的要求设计一个线段类,一个点类,按要求计算线段的长度,然后设计一个display 方法按要求输出所有的信息。类图如下

 

 

 

  • 期中考试的第二题在第一题的基础上加一个抽象类,并且增加了一个面类,要求实现多态的特性,题目要求相差不大,主要在于继承与抽象类的运用,抽象类的设计如下:
     static abstract class Element{
        public abstract void display();
      }

  注意定义抽象方法的时候,只需要声明抽象方法,不需要方法体,不用加上大括号 “ { ” 与 “ } ” 。

  类的继承:

    static class Point extends Element
    static class Line extends Element
    static class Plane extends Element

  多态的使用: 向上造型,父类的管理者可以去管理一个子类的对象 。

复制代码
     Element element = p1;
        element.display();
        element = p2;
        element.display();
        element = l;
        element.display();
        element = p;
        element.display();
复制代码

  向高手学习一个封装的保留对应位小数的方法:

复制代码

double zh(double a){
    String x=a+"";
    String z[]=x.split("\\.");
    if(z[1].length()>3) {
      x = String.format("%.3f",a);
      a = Double.parseDouble(x);
    }
  return a;
}

复制代码
  • 期中考试的最后一题,要求使用一个泛型容器类去储存 Element 类 

  类图如下:

 

  

 

 

  在主方法中,用户循环输入要进行的操作(choice∈[0,4]),其含义如下:

      1:向容器中增加Point对象

      2:向容器中增加Line对象

      3:向容器中增加Plane对象

      4:删除容器中第index - 1个数据,若index数据非法,则无视此操作

      0:输入结束

  由上一个  blog 我们已经知道用一个switch来解决这个问题可以有效节省圈层复杂度,对于使用remove删除ArrayList里的元素的时候,需要注意的是,如果该位置已经没有元素了,则不需要去进行删除操作,所以需要一个 if 语句判断去处理这个特殊情况。这部分代码如下:

复制代码
     while(a!=0){
            switch(a) {
            case 1:
                double x = in.nextDouble();
                double y = in.nextDouble();
                Point p1 = new Point(x,y);
                g.add(p1);
                break;
            case 2:
                double x1 = in.nextDouble();
                double x2 = in.nextDouble();
                double x3 = in.nextDouble();
                double x4 = in.nextDouble();
                String s = in.next();
                p1 = new Point(x1,x2);
                Point p2 = new Point(x3,x4);
                Line l = new Line(p1,p2,s);
                g.add(l);
                break;
            case 3:
                s = in.next();
                Plane p = new Plane(s);
                g.add(p);
                break;
            case 4:
                int index = in.nextInt();
                if(index<=g.geo.size()){
                    g.remove(index);
                }
                break;
            case 0:
                return ;
            }
复制代码

  这道题目的关键就在于 容器类的设计 以及 ArrayList 的泛型运用,容器类需要定义一个私有属性的 ArraList ,对于容器类自己设计的 remove 方法,需要注意的是,删除传进来的下标 index 的前一个元素。类设计代码如下:

复制代码
static class GeometryObject{
        private ArrayList<Element> geo = new ArrayList<>();
        GeometryObject(){
            
        }
        GeometryObject(ArrayList<Element> g){
            this.geo = g;
        }
        void add(Element element) {
            geo.add(element);
        }
        void remove(int index) {
            geo.remove(index-1);
        }
        ArrayList<Element> getList(){
            return geo;
        }
    }
复制代码

  二、电信计费系列1

题目类图如下:

 

 

这个系列当中的正则表达式都是比较复杂的,如果正则表达式没有设计好,就不可能拿到满分,而且就算计算过程是正确的,也会被正则表达式挡住,不能进行正确计算,对于通话记录的时间问题,需要考虑是否为闰年,该月一共有多少天,及时间等,具体正则表达式如下:

                if(data.matches("[t]-0791[0-9]{7,8}\\s"+"0[0-9]{9,11}\\s"+"((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})\\.(((0?[13578]|1[02])\\.(0?"+"[1-9]|[12][0-9]|3[01]))|(([469]|11)\\.([1-9]|[12][0-9]|30))|(2\\.([1-9]|[1][0-9]|2[0-8]))))|(((" +"[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))" +"\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s" +"((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][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]))))|(((" +"[0-9]{2})([48]|[2468][048]|[13579][26])|(([48]|[2468][048]|[3579][26])00))\\.2\\.29))" +"\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")){

 

还有一个非常关键的点在于使用SimpleDateFormat类去将输入的字符串转化成Date 类型,从而再去计算通话用时

 

SimpleDateFormat format = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");

Date st = null;
Date et = null;
st = format.parse(x[2]+" "+x[3]);
et = format.parse(x[4]+" "+x[5]);

 一个相当重要的做法是需要对容器内的对象们进行排序,我使用到的是新建一个类去实现Comparator接口,然后重写compare方法,去得到排序的效果

class UserCom implements Comparator<User>{

    @Override
    public int compare(User o1, User o2) {
        double x = Double.parseDouble(o1.getNumber());
        double y = Double.parseDouble(o2.getNumber());
        return (int)(x-y);
    }
    
}

排序使用:

users.sort(new UserCom());

第二题,第三题难度都偏易,这里不多加赘述。

  三、PTA大作业七:

 本题加入了手机用户,这就需要考虑到手机——手机通话、手机——座机通话、座机——手机通话、座机——座机通话,四种情况,类图如下:

 

 因为此题分有手机和座机用户,因此我分了两个集合,这题使用的集合是TreeSet,主要是练习一下不同集合的使用,一个用来储存手机用户,一个用于储存座机用户,分别对两个容器排序之后先输出座机容器即可。

TreeSet<User> users = new TreeSet<User>(new UserCom());
TreeSet<MUser> musers = new TreeSet<MUser>(new UserCom());

对于TreeSet容器,使用Comparator接口实现排序时,只需要在定义TreeSet的时候加上new UserCom() 即可实现自动排序,然后只需要顺序输出即可

class UserCom implements Comparator<User>{

    @Override
    public int compare(User o1, User o2) {
        double x = Double.parseDouble(o1.getNumber());
        double y = Double.parseDouble(o2.getNumber());
        return (int)(x-y);
    }
    
}

对于四种情况的正则表达式如下:

座机——座机

 

if(data.matches("t-[0-9]{11,12}\\s[0-9]{10,12}\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")){

 

座机——手机

else if(data.matches("t-[0-9]{11,12}\\s[0-9]{10,12}\\s0[0-9]{2,3}\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")){

手机——手机

else if(data.matches("t-[0-9]{11,12}\\s0[0-9]{2,3}\\s[0-9]{10,12}\\s0[0-9]{2,3}\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")){

手机——座机

else if(data.matches("t-[0-9]{11,12}\\s0[0-9]{2,3}\\s[0-9]{10,12}\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\\s((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[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]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))\\.2\\.29))\\s([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])")){

 

  四、PTA大作业八:

这题只需要考虑短信的问题难度较小,主要是正则表达式来计算短信条数,

判断输入正确格式的正则表达式如下

if(data.matches("m-[0-9]{11,12}\\s[0-9]{10,12}\\s([0-9]|[a-z]|[A-Z]|\\,|\\.|\\s)+"))

由于输入的字符串前面长度是固定的,因此可以截取后面一段然后直接计算发送短信内容的长度来计算短信条数

String h[] = data.split("-");
                String x[] = h[1].split("\\s");
                String e = data.substring(26);
                int flag = 0;
                for(int i=0;i<e.length();i++)
                {
                    if(e.charAt(i)=='-'){
                        flag = 1;
                        break;
                    }
                }
                if(flag==1){
                    continue;
                }
                //System.out.println(x[2]+" "+x[3]+" "+x[4]+" "+x[5]);

                try {
                    for(MUser i:musers) {
                        if(i.getNumber().equals(x[0])) {
                            int sum=0;
                            sum = e.length();
                            if(sum%10==0) sum=sum/10;
                            else sum = sum/10 + 1;
                            i.aaa += sum;
                        }
                    }
                } catch (Exception e1) {

                }
for(MUser i:musers) {
            System.out.print(i.getNumber());
            if(i.aaa<=3){
                System.out.printf(" %.1f ",0.1*i.aaa);
                System.out.printf("%.1f\n",100-0.1*i.aaa);
            }
            else if(i.aaa>3&&i.aaa<=5){
                System.out.printf(" %.1f ",0.2*(i.aaa-3)+0.1*3);
                System.out.printf("%.1f\n",100-(0.2*(i.aaa-3)+0.1*3));
            }
            else{
                System.out.printf(" %.1f ",0.3*(i.aaa-5)+0.1*3+0.2*2);
                System.out.printf("%.1f\n",100-(0.3*(i.aaa-5)+0.1*3+0.2*2));
            }
        }

 

 

  • 踩坑心得   

   - 开始并不了解正则表达式,完全按照题目要求一个一个去写判断输入合法的条件,一提交就发现各种问题,包括重复零输入和小数点后无数字等等,反复修改代码,在磕磕绊绊中写完第一题之后才开始了解到正则表达式,只需要一行代码就可以解决全部的输入合法问题,一行抵十几行。

     - 声明对象数组之后,每次使用该对象数组元素时都需要重新new一下,不然其为空。

     - 对于浮点型数据,判断三条边是否构成直角三角形时,使用勾股定理不能简单的写成:

if(a*a+b*b==c*c || b*b+c*c==a*a || c*c+a*a==b*b)

       要考虑浮点型数据的误差,可写成:

if(((a==b)&&Math.abs(a*a+b*b-c*c)<1e-6)||((c==a)&&Math.abs(c*c+a*a-b*b)<1e-6)||((c==b)&&Math.abs(c*c+b*b-a*a)<1e-6))

   对于抽象方法的定义,不需要定义方法体:

public abstract void display();

 

  • 改进建议

       1. 在定义方法时,需要适当的采取简便方法降低算法的复杂度,完全暴力不光复杂度高,而且代码冗长,可读性差。

       2. 添加注释,并不是每次都有充足的时间一次性完成这个程序,若不添加注释,隔了一段时间后再来看自己写的代码,发现都不知道自己在写什么,加上一些注释,方便之后的查阅和修改。 

       3. 对代码实现模块化,自顶而下,逐步细化,根据所需要求固定一个大方向,比如设计一个类,再去考虑定义方法一个一个去解决题目的要求。

       4. 尽可能减少代码复用,提高代码质量。

 

  • 总结

   ①通过三次大作业,更加理解了java这门语言,熟练了使用java语言来编写程序解决问题,尤其是大作业三,让我对面对对象程序设计初窥门径。

   ②通过大作业三,学到了正则表达式,合理的运用正则表达式能够节省很多不必要的分支判断,提高效率,使代码更加简洁。

   ③也因为三次大作业了解到了许多关于字符串操作方面的方法,如split、substring、charAT等等,对字符串操作更加娴熟。

   ④关于类的设计与类的应用,也有了一个新的层面的认知和经验,这是面对对象的魅力所在。

   ⑤这三次作业,让我的编程能力更进一步,了解到更多不曾知晓或者不够熟悉的知识点,初步形成了面对对象编程的思想。



这篇关于BLOG - Three的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程