java三阶段总结(家用电路模拟)
前言
- 第六次题目集
- 知识点:
抽象类,抽象类不能被直接实例化!有抽象方法,抽象方法没有方法体,由继承它的子类提供具体实现。抽象类可以看作是一种模板,子类在实现接口方法时,必须实现接口中的所有抽象方法,除非子类也是抽象类
在抽象类和接口中,多态体现为父类引用变量可以指向子类对象,定义了一个父类类型的引用,指向新建的子类类型的对象,由于子类是继承他的父类的,所以父类类型的引用是可以指向子类类型的对象的,多态成员变量:编译运行看左边,多态成员方法:编译看左边,运行看右边
题量:正常
难度:正常,但对于上一题是有比较大的阶段的提升的,因为这个互斥开关需要设计的时间比较长,然后那个并联包并联也不是稍微想想就能想出一个比较好的方法
第七次题目集
- 知识点:
接口是100%的抽象类,方法全抽象,属性全为全局变量,主要体现在多态上
接口中没有成员变量,只有公有静态常量 ,在接口中定义公有静态常量时,必须在定义时就赋初值
接口也不可以被实例化。只能通过多态的方式实例化“子类”对象,接口中不允许存在代码块,也没有需要初始化的成员,因此接口没有构造方法(构造器)
接口的子类(实现类) :
可以是抽象类,也可以是普通类
对于抽象实现类,可以不用实现接口的所有方法,因为抽象类本身容许存在抽象方法,语法上是通过的
对于普通实现类,要求实现接口的所有抽象方法
题量:正常
难度:很好,对比上一题难度断层提升,需要的设计的时间多,需要改的部分也很多,很多奇奇怪怪的小点需要考虑,不过撞上四六级和期末周可能是很多人没花时间写的原因吧
二极管是个奇怪的器件,理解都要好一会
管脚电压的显示这个要求确实是比较麻烦,因为我个人是把引脚类架空了,在之前的题目里形同虚设,虽然写了但是不管是加入引脚还是计算引脚都没写好,只是用上了电压差来代替这个设计,所以改这里需要花的时间比较多。
设计与分析
1.家居强电电路模拟程序-3
题目分析:
新增互斥开关,一定会一开一断
新增受控窗帘,这个要求所有灯的亮度才能powerOn
注意的点:
-
注意互斥开关有限流电阻!别想当然的以为短路了
-
互斥开关可以正反接
-
互斥开关的引脚输入很有特点,需要注意输入什么引脚的时候加入电路

-
窗帘:如果电路中没有灯或者灯全部关闭,当电压低于50V,光照强度为0,窗帘处于全开状态, 所以是100%
-
受控窗帘应该在整个电路通电一遍了,求出总光照,再自己通电,也就是说,要单独通电两次
代码分析:


-
最高复杂度竟然是那个工厂类,新建并return 各种电器的方法。最大深度正常就行。
-
类
twiceSwitch是一个自定义的开关类,继承自control类,并且设计用来控制两个开关,因为并没有分两个类来设计,所以两个串联电路加入的电器都是同一个H -
设计两个电路名属性,分别对应2号开关和3号开关,但是在
getThisAccess(String name)函数里有点复杂,getThisAccess(String name):这个方法根据提供的设备名称和当前的访问权限,返回该设备的访问权限。如果权限是1,onName设备的权限是1,offName设备的权限是-1;如果权限是-1,情况则相反。
public void setCruitName(appliance a,int id){
String name = a.getName();
if(id == 2){
onName = name;
((circuit)a).addAppliance(getName());
}
else if(id == 3){
offName = name;
((circuit)a).addAppliance(getName());
}
else return ;
}
- 互斥开关只有在第一次时加入总设备集合,后面都会进去
setCruitName函数,设置电路对应名
if (!apps.applianceMap.containsKey(name)) {...}
else{
appliance appl = apps.applianceMap.get(name); /*存过也要改,会反接*/
if(type == 'H'){
((twiceSwitch)appl).setCruitName(a,pp.getId()); /*这里直接传A可能有点不好,但是更简洁*/
}
- 总光照度:设计一个全局变量
static double lampLux = 0,全员通电一遍,计算出所有的光照值,然后在窗帘的输出函数内再通电一次,求出输出结果,这样就不用设计一个集合来存储灯或者窗帘了
public void powerOn() { /*通电函数*/
double D = getDVol();
double lux = appliances.lampLux;
if(D >= 50){
...求开关程度
}
public String getShow() { /*输出函数*/
powerOn();
return String.format("%d%%", scale);
}
- 其次我设计了乱序输入,但是没想到下一次没有用到haha

2.家居强电电路模拟程序-4
题目分析:
新增二极管,正向导通,反向截止,相当于一个开关
并联电路M中的串联电路可以包含别的并联电路
增加管脚电压的显示,这个比较麻烦
电流限制与短路检测,最后来实现即可
设计思路:
-
首先需要用到引脚,而互斥开关有三个引脚,最后还要按引脚编号排序输出,所以每个电器都设置了引脚list
public List<Pin>pinList;注意赋值电压时要按顺序赋值 -
因为电路的首末电位需要使用,就是当这个电路断开时,需要用到末电位,当是并联的递归函数,要用到首电位,所以需要为电路设置引脚
-
注意一个情况,当一个断路里又很多类开关器件(串/并电路,开关,互斥开关,二极管),那么每个开关的顺序很重要,2个及以上,第一个开关的右引脚到最后一个开关的左引脚会为0,最后一个开关的右引脚为电路末电位,我的设计思路是,把串联电路的
access属性提升为不连通段数属性,之前是连通为1,不连通为-1,现在是出现一个不通器件就+1,就可以统计本电路的不连通段数,为所有电器设置一个number属性,不连通就赋值为此时父电路的access值,这样后期设置引脚电压时就可以将number和access比较来判断是第一个/最后一个开关。 -
对于二极管,我的理解是,所有二极管的初始化
access设为1,如果父电路是通路,那么有反接的二极管就算断路,access设为-1,即使父电路最后没电流也没事,最后就正常按两端电压赋值二极管导通”conduction”,截止”cutoff”,两端有电压差就是断路,没有就再根据要求再分析。 -
对于互斥开关,上次设计成一个类,但是发现要传递电压,两个类会更加方便,所以更新了互斥开关的代码,设计成父互斥开关和子开关类,对于引脚的加入去重需要有一些设计
-
然后因为调速器只有一个,上次是直接不加入电路里的,所以调速器的引脚电位计算直接在通电输出时计算即可
-
计算电流,设置了一个全局变量,记录干路电流
public static double IAsum = 0.0;,同时也方便调速器取用,对于串联电路,电流相等,对于并联电路,要分通路,断路,短路三种情况讨论,而且要注意多个短路电路也会分流
代码分析:


-
不需要在每个串联电路里设置前后引脚电压,并联里设置和串联的递归里设置就可以了
-
不连通段数的设置函数,放在串联的isAccess函数里
for(appliance a : seriesApps){
int Access = -1;
a.isAccess();
Access = a.getAccess();
if (Access == 1 ) { /*串联电路的不通是大于1,并联电路不通是-1*/
RSum += a.getR();
} else {
setR(-1); /* 断路 */
nowAccess++; /*记录这个电路总的不连通段数,后期比较*/
a.setNumber(nowAccess); /*记录这是第几个不连通段数*/
}
}
- 二极管的判断函数
public void powerOn(circuit a) {
if (pinList.get(0).getVol() != pinList.get(1).getVol()) {
show = "cutoff";
} else if (pinList.get(0).getVol() == 0) {
show = correct == 0 ? "cutoff" : "conduction";
} else {
show = "conduction";
}
}
- 设置引脚电压的函数,其实没有考虑如果没有输入两个引脚的情况,但是好像也没有这个测试点哈哈哈
/* 为元件配两端电压,也配电路!!! */
public void setPinVol(appliance a){
if(a.pinList.size() < 2){ /* 不计算只有一个引脚的电器,但是有漏洞哦 */
Pin p = null;
p.getId();
return ;
}
/*双刀开关可以以相加的形式*/
Pin in = a.pinList.get(0);
Pin out = a.pinList.get(1);
in.setVol(nowVol);
out.setVol(nowVol - a.getDVol()); /* 正常设置右电压 */
if(out.getVol() < 0){
out.setVol(0);
}
int order = a.getNumber();
int maxPart = getAccess();
if( order == 2 ){ /*第一个断路,也可能是最后一个断路*/
out.setVol(0);
}
if(order == maxPart && maxPart != 1){ /* 最后一个断路 */
out.setVol(pinList.get(1).getVol());
}
nowVol = a.pinList.get(1).getVol();
}

踩坑心得
1. (家居强电电路模拟程序-3) 对应互斥开关,在串联电路里通电,需要多判断这个电路对应的是2号还是3号

2.(家居强电电路模拟程序-3) 被卡在多并联电路了,自己怒写5个并联6个电路的测试点,才发现是自己的并联电路电阻计算错误,我用的不是标准并联计算式,是化简式,然后发现这个只对两个并联有用


改为,就可以了,然后发现不是我一个人有这个问题哈哈哈
for(String name : parallelApps){
appliance a = appliances.applianceMap.get(name);
if(a.check == 0)
a.isAccess();
if(a.getAccess() == 1){
flag ++;/* 标志总并联电路连通不连通,1是连通 */
if(a.getR() != 0) { /* 有电阻的串联电路 */
RSum += 1/a.getR();
}
else{ /* 无电阻,短路,直接break */
setAccess(1);
setR(0);
return ;
}
} /* 不连通,找下一条,这里和串联不一样 */
}
if(flag == 0){ /* 没有一条路连通 */
setAccess(-1);
setR(-1);
}
else{
setR(1 / RSum);
}
6.(家居强电电路模拟程序-4) 在按顺序赋值引脚电压的函数内,发现串联电路在设置两端引脚电压后,nowVol会被设为此电路的末电压,然后再给电路通电计算内部电器电压时会出错
解决:在串联的powerOn函数里把电路nowVol设为串联的首电压
circuit.nowVol = pinList.get(0).getVol();
/*注意如果串联电路进入setPin函数里,会把nowVol设为电路末尾电压,因为串联内部还要powerOn*/
7.(家居强电电路模拟程序-4) 如果串联里面的并联断路了,他的右端电压也应该为父电路的右电压
解决:

不过这个解决没到点子上,后续完全解决是把并联电路完全的当做一个器件放进设置引脚电压的函数里,就可以了。
8.(家居强电电路模拟程序-4) 互斥开关引脚输出问题,因为设计了两个类,在输出父类时会一口气输出六个引脚电压,而加入引脚的函数是普适全体电器的不好修改
解决:输出时先进行clear,。再把子类的引脚去重加入
public void showPinVol(){
pinList.clear();
for(Pin p: onSwitch.pinList){
addPin(p);
}
for(Pin p: offSwitch.pinList){
if(p.getId() != 1){
addPin(p);
}
}
super.showPinVol();
}
9.(家居强电电路模拟程序-4) 并联电路如果最后是短路,且出现多个短路,那么各个短路电路也会分流
解决:设置一个属性记录子短路电路个数,如果大于0,那么此时并联电路就是短路,为每个短路电路赋值平均电流即可
if( RSingle == 0 ){ /*短路*/
a.setDVol(0);
a.setIA(getIA()/shortSum);
}
else if(RSingle == -1){ /*断路*/
a.setDVol(0);
a.setIA(0);
}
else{ /*通路*/
a.setDVol(this.getDVol());
a.setIA(a.getDVol()/RSingle);
}
改进
个人代码改进策略:
- 代码结构优化
- 可以考虑将重复代码抽取到基类或工具类中,减少代码冗余。例如,
powerOn方法中的电压判断逻辑可以通过模板方法模式进行优化。 - 抽象类和接口:将开关和调速器的公共行为抽象到接口或抽象类中,提高代码的重用性和灵活性。
- 日志记录:添加日志记录功能,记录每次设备状态的变化,方便调试和维护。
- 属性的访问控制
- 对于属性的访问应使用 getter 和 setter 方法,直接访问属性会破坏封装性。
- 方法的实现
getShow方法中调用powerOn,可能会重复执行不必要的逻辑,应确保此调用是必要的。
- 异常处理
powerOn方法中对电压的判断没有考虑异常情况,如电压超出范围或负值,应添加适当的异常处理。
-
命名约定:遵循 Java 命名约定,类名应该使用 PascalCase,比如
Circuit而不是circuit -
代码的扩展性
- 使用设计模式:
-
命令模式:可以将每个控制操作(如开关、调速)封装成命令对象,便于扩展和维护
-
策略模式:对于不同的调速策略,可以使用策略模式来动态切换不同的调速实现
-
- 单例模式:
- 对于某些全局唯一的控制器类,可以使用单例模式确保只有一个实例存在,避免重复创建带来的资源浪费
总结
学到了:
-
对抽象类的理解加深了,抽象类不能被直接实例化!有抽象方法,抽象方法没有方法体,由继承它的子类提供具体实现。抽象类可以看作是一种模板,子类在实现接口方法时,必须实现接口中的所有抽象方法,除非子类也是抽象类
在抽象类和接口中,多态体现为父类引用变量可以指向子类对象,定义了一个父类类型的引用,指向新建的子类类型的对象,由于子类是继承他的父类的,所以父类类型的引用是可以指向子类类型的对象的,多态成员变量:编译运行看左边,多态成员方法:编译看左边,运行看右边
-
接口是100%的抽象类,方法全抽象,属性全为全局变量,主要体现在多态上
接口中没有成员变量,只有公有静态常量 ,在接口中定义公有静态常量时,必须在定义时就赋初值
接口也不可以被实例化。只能通过多态的方式实例化“子类”对象,接口中不允许存在代码块,也没有需要初始化的成员,因此接口没有构造方法(构造器)
-
接口继承关系
类与接口是实现关系,支持“多实现”,即一个类可实现多个接口
接口与接口是继承关系,java 支持接口的多继承,即一个接口可以同时继承多个接口
继承体现的是“is a”的关系,父类中定义共性内容。实现体现的是“like a”的关系,父接口中定义扩展内容
-
接口的子类(实现类) :
可以是抽象类,也可以是普通类
对于抽象实现类,可以不用实现接口的所有方法,因为抽象类本身容许存在抽象方法,语法上是通过的
对于普通实现类,要求实现接口的所有抽象方法
学期总结
收获:
-
真的能一点点的感觉自己在进步,这门java课,最主要的就是从面向过程编程转变为面向对象编程,因为我没有学c++的类与对象,所以这是我第一次接触,还记得刚开始上手连该建什么类都不知道,现在已经能熟练写完一个复杂的pta设计啦~~~撒花!
-
感觉PTA的题目对我的训练很有效,难度设计的很好,不断增大,但是却越写越熟练,设计上,也从边敲代码边设计转变为理清思路再上手,效率变高的同时,设计的也更加的清晰完整。而且感觉这学期的训练,我对于边界情况的考虑和处理越来越好了,从学期初设计好第一版提交错一堆小测试点一直改,到后期第一版提交改的越来越少了,一些坑也会自己规避。
-
很多概念已经转化成实例印在脑子里了,说继承就会想到一个适用的模型,说到方法重写就会想到子类对象访问一个方法会先子类成员范围找,如果找不到就在父类成员范围找,如果都没有就报错,不隔代查,说到抽象类就会想到抽象方法不能再使用
private,final或者static关键字来修饰,子类必须重写抽象方法,说到多态就会想到成员变量:编译运行看左边,成员方法:编译看左边,运行看右边,说到模式就想到组合模式和工厂模式, 装饰器模式等等等等。 -
哦!还有一点意想不到的收获,现在在做课设,我用qt都更熟练了haha,因为类与对象思想学会了也同样套在c++里,比之前有框架多了。
心里话:
-
感觉老师的教学安排都挺好的!讲的也很好,有不会的自己再上网查查资料就很OK了,而且会让人有学java的热情,而且如果每次都认真去写pta,那真的能进步飞速,这个写blog也很有意义,回看代码,重新理一遍设计思路,回顾一下踩坑经验,理解的也更深刻了。
-
但是!对于java实验,我感觉还是可以改进一下,就是有时候题目有点看不懂?就比如说这个第四次的实验题目吧,全文看下来想申请中译中haha~要求比较前后矛盾,反正不止我一个人有这个问题,所以说一下
浙公网安备 33010602011771号