java二阶段总结(电路模拟程序)

前言

  • 第四次题目集

    1. 知识点
    • 开始应用继承在大题目中,但对于方法重写,多态的要求未涉及

      继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。

      继承是指在原有类的基础上,进行功能扩展,创建新的类型。

    • 方法重载:第一题setYearsetMonthsetDay方法通过接受不同参数来实现方法重载。

    1. 题量:正常

    2. 难度:有个测试点很难想到,卡人比较多,其他的都还好,只要学会继承就会好写

  • 第五次题目集
  1. 知识点
  • 加深继承的学习,很多地方需要应用方法重写,多态更是基操,同时引入接口,第二题ArrayList 实现了 List 接口,提供了添加、获取元素等方法。

    • 方法重写形如powerOngetShow

    • 继承形如series类继承自circuit类,control类继承自appliance类,SwitchgearControlcontiControlequiplampfan等类继承自它们的父类。

    • 多态例子:appliance类和其子类展示了多态性,其中appliance作为父类,其方法在子类中被重写。

  • 同时也系统的学习了组合模式(又叫作整体-部分模式,它将对象组合成树状的层次结构,用来表示整体-部分的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型模式)

  1. 题量:正常

  2. 难度:如果是不思考后续设计直接写,应该比较简单,但老师再三强调需要思考后续设计,所以在设计上花费了很多时间 ,对比答题判题系列,电器系列的难度有断层增加,之前可以直接思考大概思路就上手,写代码占比高,但这题开始就要先花大量的时间理思路,反而后面上手写会比较轻松。确实是设计大于代码,但由于题目的要求不会特别高,比如,只有串联电路,不会乱序输入等等,所以也算是很好的二阶段题目

  • 第六次题目集

    1. 知识点
    • 抽象类:当一个类中给出的信息不够全面时,它给出的信息不足以描绘出一个具体的对象,不能实例化该类,这种类就是抽象类

    • 抽象方法:当父类需要定义一个方法,却不能明确该方法的具体实现细节而定义的只有方法声明,没有方法体的一类方法统称为抽象方法,抽象方法用关键字abstract修饰

      如果某个类中已经出现了抽象方法,那这个类必须定义成抽象类

    • 多态:多态体现为父类引用变量可以指向子类对象,定义了一个父类类型的引用,指向新建的子类类型的对象,由于子类是继承他的父类的,所以父类类型的引用是可以指向子类类型的对象的

      • 继承或实现:在多态中必须存在有继承或实现关系的子类和父类

      • 方法的重写:子类对父类中的某些方法进行重新定义(重写,使用@Override注解进行重写

      • 基类引用指向派生类对象,即父类引用指向子类对象,父类类型指子类对象继承的父类类型,或实现的父接口类型

    1. 题量:正常

    2. 难度:不错不错,需要的设计的时间多,所以测试点没有偏的,改错时间少,所以总时长也不会太多

    虽然乍一看给人无从下手的感觉哈哈,比如算电阻,算连通性,类之间的关系等等,每一种都让人刚开始毫无头绪。所以,需要花费在设计上的时间更长了,不仅要考虑方法能不能,还要考虑方法好不好,等设计好了就会知道该写什么属性方法,方法大概该怎么写,上手写就很快了,而且有个好处就是没什么错

    对比第一题,需要考虑的功能变多了,而且特别适合使用抽象类了,所以这也是一个特别好的锻炼抽象类使用的题目,

    对比第一题,这一题更需要画流程图,要在草稿上线判断各种情况,即使第一题设计的很详细了也需要改动很多地方,反正我至少算是中改

设计与分析

1.答题判题程序-4

题目分析:

在3的基础上,新增单选题,多选题,填空题,最主要的是,输入顺序会改变了

多选题和填空题出现全对,半对,错的判断,这会影响答案的存储结构和分值的计算。

必须用继承

注意的点:

  • 题目会在试卷之后再输出!这个要求我上一次的代码不能实现,上一次写的是输入试卷后直接把题目对象加到试卷对象里,这样就会导致加入题目时,如果题目不存在,最后就会输出题目不存在,需要改动

  • 多张试卷信息,所以需要建立试卷类,试卷list类来存储

  • 输出要按学生信息来按序输出,而输入乱序,所以需要将答卷对象按一定顺序存储

  • 因为必须用继承,所以需要学会多态的用法

代码分析:


  1. 刚开始使用继承,十分不熟悉,在对于题目,选择题,填空题,普通题的关系时,虽然使用了继承和多态(将choiceQuestiontextQuestion初始化为question加入questionlist中),但是没有使用方法重写,而是在每个子类里多写了两个新的isRight的方法(isTextRightisChoiceRight)达成和父类一样的功能,这让后面判断题目正确性比较麻烦,离谱到还新建了两个textQuestionListchoiceQuestionList类哈哈

  2. 对于多态的使用也理解不透彻,典型半桶水代码

  3. 在最开始就应该修改

case  "#Z:":
                    // 检查选择题格式是否正确
                    if(st.questionStandard(test, cstr) == 1){
                        choiceQuestion qq = new choiceQuestion(cstr);
                        clist.addChoiceQusttionmp(qq.getId(),qq);
                    }
                    else{
                        flag = 0;
                    }
break;
改为:
                        question qq = new choiceQuestion(cstr);
                        qlist.addQusttionmp(qq.getId(),qq);
  1. 上一次是在试卷输入时判断题目,修改为在输入end以后再加入并判断题目即可,这种题目需要常常用到标志入list但本体后面入list的方法

新掌握:

使用到了set容器,主要是可以键-值查找而且可以去重,用来记录多选题答案,之后判断正误也由set来查找

public Set<String> choiceQuestionSet = new HashSet<>();
  • 部分顺序图:

2.家居强电电路模拟程序-1

题目分析:

  1. 输入一长串的连接信息,设备信息不单独输入,包含在连接信息中。

  2. 含有控制设备模拟:开关、分档调速器、连续调速器,受控设备模拟:灯、风扇。

  3. 按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇的顺序依次输出所有设备的状态或参数。

注意的点:

  1. 开关的开闭会导致整个电路断路!所以需要在最开始进行一次连通性判断,再进行通电操作

  2. 控制设备只有输入电位的要求,而受控设备需要电压差,后者对引脚的区分不明确

  3. 分档调速器理解错了啊啊,0--2(3/4)档,不是没有1档!

  4. 连续调速器是两位小数输出,其余都是整数输出(截尾输出,非四舍五入

  5. 设备要按序输出,所以应该设计优先级

  6. 会有没有接线的引脚,就默认接地,电压为0V,所以在刚开始初始化的时候就要把引脚设为0V

代码分析:


  1. 由于在pickPin函数中使用了switch-case的方法导致方法最大复杂度增加,虽然判断类型问题一次性解决很方便,但下次还是要考虑将他们分开写

  2. 对于按序输出,当时思考到会不会后续需要对各种设备进行一系列别的操作,所以为每个设备设立了list,方便输出了但同时也复杂了,第二次改为优先级输出好多了

  3. 几乎未使用到受控设备和控制设备大类,所以这可能就是我某个方法复杂度高的原因吧。

  4. 关键代码:

被子类重写的通电方法,在存储所有电路信息后,再一次性为他们通电,更新这个电路的“当前电压值“(太鸡肋第二次代码已弃用),而且第二次也将串联电路继承电器类,整个方法都将改为powerOn

 public void charged(){
        ...
        for(appliance a : this.getApplianceList()){        // 遍历电路中的所有电器设备
            Pin in = a.getInPin();     // 获取电器设备的输入引脚
            in.setVol(getNowVol());    // 设置输入引脚的电压为电路的当前电压
            a.powerOn();    // 电器设备通电
            Pin out = a.getOutPin();    // 获取电器设备的输出引脚
            setNowVol(out.getVol());    // 设置电路的当前电压为输出引脚的电压
        }
    }
  1. 对每个受控设备,都设置一个获取电压差的方法,写在equip
public double getDVol() {
        Pin in = getInPin();    // 获取输入引脚
        Pin out = getOutPin();  // 获取输出引脚
        if(in.getName().equals(out.getName())){ // 如果输入和输出引脚名称相同,则电压差为0
            DVol = 0;
        }
        else{
            DVol = abs(in.getVol() - out.getVol()); // 否则,计算并返回它们之间的绝对电压差
        }
        return DVol;
    }

新掌握:

  1. 组合模式:

抽象构件(Conponent)角色:主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。抽象构件声明访问和管理子类的接口;
树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中声明的公共接口。
树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含
Add()、Remove()、GetChild() 等方法。

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;

  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

在本代码中具体的应用就是索引设备直接或间接继承appliance类,甚至串联电路与并联电路

  • 部分顺序图:


2.家居强电电路模拟程序-2

题目分析:

需要考虑电阻:白炽灯的电阻为 10,日光灯的电阻为 5,吊扇的电阻为 20,落地扇的电阻为 20

新增模拟落地扇,新增并联电路

线路中包含多个串联起来的并联电路

注意的点:

  1. 并联电路只会有串联电路,不会出现单个设备的情况

  2. 并联信息所包含的串联电路的信息都在并联信息之前输入,不考虑乱序输入的情况

  3. 短路要考虑,而且当正常处理,因为不会产生无穷大的电流烧坏电路

  4. 调速器的输入端只会直连VCC,而且最多只一个调速器!后期可能会连在串联电路里!因为题目只说了不在并联电路里,可能在第一个串联单路里。

  5. 不考虑输入电压或电压差超过220V的情况,所以下次应该要考虑了

  6. 并联电路和串联电路的输入相似但是是不同的!会一个框输入三个以上!

代码分析:


  1. 对于本题,多使用抽象方法,定义了总抽象父类,要么实现父类所有的抽象方法,要么子类本身也定义成抽象类

总抽象父类appliance,下面定义了controlequip两个抽象子类,剩下的都是实体类

  1. 对比上一次,架空了Pin类,因为有电压和电阻,不需要引脚来传递电压,而是直接由函数递归来实现电压的分配,但还是没删pin类,因为不知道下次有没有用哈哈

  2. 关键代码:

  • 对于调速器,因为题目规定只有一个而且连着VCC,所以我将其拆开来看,相当于电源的一部分,而不把其加入电路内设备集合中(不过要加入总设备集合appList里输出),在设备集合类里定义一个记录调速器名字的属性,在输入时发现调速器,就记录,最后递归求电压时先作用调速器

    class appliances{
        public String adjustName = "NOTHING";   /*  调速器名字   */
        public List<appliance>circuits;        /*  装电路,最后一个是总电路(需改)   */
        public HashMap<String,appliance> applianceMap;        /*  装所有设备,包括电路   */
        public List<appliance> appList;        /*  装所有设备,不包括电路  */
    ...
    
  • 对于按序输出,需要按照优先级和名字排序

     public void endShow(){
            Collections.sort(appList, new Comparator<appliance>() {      /*  排序,先比较优先级,再比较名称   */
                public int compare(appliance a1, appliance a2) {
                    if(a1.getPriority() != a2.getPriority()){
                        return a1.getPriority() - a2.getPriority();
                    }
                    else{
                        return a1.getName().compareTo(a2.getName());
                    }
                }
            });
    
  • 对于并联电路的电阻计算

    短路:Access = 1,R = 0

    没有一条路连通: Access = -1, R = -1

    只有一条通路:Access = 1, R = RSum

    正常:R = RMultiply / RSum    (其实用倒数应该更方便)

新掌握:

  1. 学会使用工厂模式,属于类创建型模式,通过静态方法处理不同传入参数,从而创建不同具体产品类的实例,外界调用工厂类的静态方法,传入不同参数创建不同具体产品类的实例
  1. 将对象的使用和创建过程分离开,实现解藕。客户端不需要关注对象是谁创建的、怎么创建的,只要通过工厂中的静态方法就可以直接获取其需要的对象。

  2. 将初始化实例的工作放到工厂里执行,代码易维护, 更符合面向对象的原则,做到面向接口编程,而不是面向实现编程。

class applanceFactory {
    public static appliance getAppliance(char c, String name) {
        switch (c) {
            case 'K':
                return new Switch(name);
            case 'F':
                return new gearControl(name);
            case 'L':
                return  new contiControl(name);
            case 'B':
                return  new filamentLamp(name);
            case 'R':
                return  new sunLamp(name);
            case 'D':
                return  new ceilingFan(name);
            case 'A':
                return  new floorFan(name);
            default:
                return null;
        }
    }
}

可以再继续学习抽象工厂类,降低耦合度。抽象工厂模式将具体产品的创建延迟到具体工厂类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而降低系统耦合度,有利于后期的维护和扩展。使用静态工厂方法,可以形成基于继承的等级结构。

  • 部分顺序图:

踩坑心得

1.(答题判题程序-4) 在一个函数需要两个matcher的时候,搞混了matcher

2.(答题判题程序-4) list的初始化不要忘记了,记得在构造函数中添加,不然会报错!

Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "java.util.List.add(Object)" because "this.formQuestions" is null

3.(答题判题程序-4) 最后被卡的测试点真的很难想到,一直在猜,甚至都把多余空格的输入改的格式化了,还是不对

原输出

改后

之后才意识到是填空题不需要删除答案前后空格问题,而这个也分两种情况:

  1. 输入有值的情况下,你前后的空格不能删

  1. 输入为空或者全部空格的情况,按原判断流程判断,删除全部空格

输入全空格,输出

4.(家居强电电路模拟程序-1) 对于分档调速器,没有限制档位最高值,导致上调||下调过多错误,之后在方法体内设置到达最高级||最低级后调整无效的操作

5.(家居强电电路模拟程序-1) 最后被卡的一个测试点是关于开关的,想岔了,对于开关在电器之后的情况,如果开关没闭合,并不是说电器能输入220v,而是电压为0v,当时理解成后面断路就为0v,前面还是220v

改为,在总通电的charged函数中添加连通性判断

for(String sw : switchss.switchTreeMap.keySet()){        // 遍历 switchss 中的所有开关
            Switch swi = switchss.switchTreeMap.get(sw);    // 获取 switchss 中的开关
            swi.powerOn();   // 通电开关
            if(swi.flag == -1){
                setNowVol(0);
                break;
            }    // 如果开关的 flag 为 -1,则将电路的当前电压设置为 0
        }

6.(家居强电电路模拟程序-2) 因为设计的时候花了比较多的时间,所以该规避的情况很多都规避了,最后被卡的测试点就是短路的其中一种,return本来写的break,导致R被赋值为0后再次被赋值为错误的数,而这个还是比较难发现的,因为如果第一条就是短路,那么是对的,只有是中间或末尾的串联电路短路,才会出错。

public void isAccess() {        /*  并联电阻,连通计算,不连通跳过 */
...
        for(appliance a : parallelApps){
            if(a.getAccess() == 1){
                flag ++;    /*  标志总并联电路连通不连通,1是连通,原来flag = 0;*/
                if(a.getR() != 0) {        /*  有电阻的串联电路 */
                    RSum += a.getR();    RMultiply *= a.getR();
                }
                else{                    /*  无电阻,短路,直接break */
                    setAccess(1);    setR(0);    return ;    }
            }
            else{    continue;  }             /*  不连通,找下一条,这里和串联不一样 */
        }
        if(flag == 0){                  /*  没有一条路连通 */
            setAccess(-1);    setR(-1);
        }
        else if(flag == 1){    setR(RSum);    }      /*    注意只有一条通路的话算串联!   */
        else{    setR(RMultiply / RSum);    }
    }

改进建议

个人代码改进策略:

  1. 性能优化:对于频繁调用的方法,可以考虑优化其性能,例如使用缓存或其他优化技术。

  2. 方法拆分:如果某个方法过于庞大,可以考虑将其拆分为多个小方法,每个方法处理一个特定的功能。

  3. 类的设计和继承:

  • 考虑使用接口来定义一些通用的行为,例如Powerable接口来表示可以通电的设备。

  • 优化类的继承结构,确保每个类的职责清晰,避免不必要的继承。

  1. 代码重构:将重复的代码片段抽象成方法,减少代码冗余。

  2. 代码清晰性:

  • 使用更具描述性的变量名和方法名,以提高代码的可读性。
  • 对于复杂的逻辑,添加注释来解释代码的目的和逻辑。
  1. 解耦:减少类之间的依赖,比如appliances类不应该直接操作appliance类的内部状态,而是通过方法调用。

总结

学到了:

  • 学会了方法重写,就是当子类需要父类的功能,而子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容,方法重写调用的是子类的方法,如果想调用父类的原方法,写:

    super.isRight(answer);
    

    但是要注意私有方法不能被重写(父类私有成员子类是不能继承的),子类方法访问权限不能更低

    • 通过子类对象访问一个方法会先子类成员范围找,如果找不到就在父类成员范围找,如果都没有就报错,不隔代查
  • 对继承的构造方法理解加深了,子类中所有的构造方法默认都会访问父类中无参的构造方法!如果没有无参构造方法,可以用super(参数)在子类无参方法调用父类有参方法

    • 子类初始化之前,一定要先完成父类数据的初始化

    • 每一个子类构造方法的第一条语句默认都是:super(),自己再写也可以

  • 学会了抽象类与抽象方法的使用,抽象类中的成员只比非抽象类多一种抽象方法,其他都和非抽象类一样。

    • 抽象方法不能再使用privatefinal 或者static关键字来修饰,即abstract不能与privatefinalstatic共同出现

    • 若父类中定义了一个抽象方法要求其所有非抽象子类都必须重写该抽象方法

  • 学会了组合模式和工厂模式

  • 对多态的理解加深,多态成员变量:编译运行看左边,多态成员方法:编译看左边,运行看右边

    1. 子类和父类存在同名的成员变量时,访问的是父类的成员变量
    2. 子父类存在同名的非静态成员方法时,访问的是子类中重写的方法
    3. 子父类存在同名的静态成员变量成员方法时,访问的是父类的成员函数
    4. 不能访问子类独有的方法

展望:

  • 回看第一次写继承的代码(答题判题程序-4),真的很搞笑,什么都是学了一半,半懂不懂,现在已经能熟练地使用啦!甚至到了要学新的关系而换掉继承有点依依不舍www,看到自己的进步很好,不过对于继承,现在只会考虑能不能用继承,而没怎么考虑适不适合用继承,还需要更熟练的用其他关系才行呢

  • 一阶段答题判题系列可以直接思考大概思路就上手,写代码占比高,二阶段电器系列开始就要先花大量的时间理思路,反而后面上手写会比较轻松。需要设计大于代码,设计好了,上手敲500行也能一气呵成,要不然写两个调用关系的方法都磕磕绊绊,以后也要多花专精一下设计,选用合适的结构,23种设计模式,需要多学学。

  • 要多学学接口呢,现在用抽象类,感觉对接口的运用还不熟练

对课程改进建议:

  • 想写点必须用接口的题目

  • 老师建议的类的关系很有帮助,按这样设计确实理的比较快,不过感觉自己对这些关系的掌握还是不太理想,会用,但没那么会想到

posted @ 2024-11-23 18:26  夏日救星  阅读(96)  评论(0)    收藏  举报