第二次博客作业
前言
本次博客是对pta题目集4~6的一些总结。
这三次的pta作业包括前面多个小题来提高对java语法的熟练度,以及不断迭代的最后一道题。题量虽然不算多,但难度逐步加大,涵盖知识点多,测试点也越来越细致,经历了从基础到复杂、从简单到深入的学习。这三个题目集的知识点涵盖了数据的存储、封装、继承、多态,以及越来越复杂的面向对象设计和代码优化,收获颇丰。
设计与分析
题目集4:
(一)校园角色类设计:
这道题主要考察了继承的概念,以及如何使用继承来创建新的类,这些新类可以继承父类的属性和方法。
(二)设计一个学生类和它的一个子类——本科生类:
考察继承的概念,以及掌握构造方法的作用,以及如何定义和使用构造方法来初始化对象的属性。此外也考察了对Java中的访问权限修饰符(protected, private)及其作用的理解与运用,要掌握如何使用访问权限修饰符来控制类成员的可见性。
(三)答题判题程序-4:
题目:这次作业在上一次基础上支持输入选择题和填空题的题目信息,每题一行,多题多行。选择题格式为“#Z:”后跟题目编号,接着是“#Q:”后跟题目内容,最后是“#A:”后跟标准答案,多选题答案用空格分隔。填空题输出结果除了对错,还有“partially correct”表示部分正确,评分规则为完全匹配得满分,错误或无答案得0分,部分正确得半分且只取整数部分。输入顺序不受限,可以任意先后输入各类信息。对于多张试卷,输出时先按学号排序,再按试卷号排序。
根据题目设计了五个类:
Question类:
这个类代表一个测试题目,可以是单选题、填空题或多选题。
它有两个构造函数,一个用于单选题和填空题,另一个用于多选题。
checkAnswer方法用于检查答案是否正确,并返回得分情况。
checkAnswerSingle是一个私有方法,用于检查填空题的答案是否部分正确。
Test类:
代表一个测试,包含多个Question对象。
提供了添加问题、检查问题是否被删除、获取问题分数、获取总分和检查是否包含某个问题的方法。
Student类:
代表一个学生,包含学生ID和姓名。
AnswerSession类:
代表一个答题会话,包含测试、学生答案和分数。
提供了添加答案、检查答案、打印问题和结果、打印分数的方法。
Main类:
主类,包含main方法,用于读取输入并处理不同的命令。
根据输入的命令,调用不同的处理方法,如handleQuestion、handleTest、handleStudent、handleAnswerSession等。
最后,它会对结果进行排序并打印每个学生的分数。
知识点:在处理正则表达式时,关键在于对输入文本进行恰当的分割和解释;接着是设计合适的数据结构,比如利用映射(Map)和列表(List)来组织和维护数据;此外,还包括排序和对比操作,比如对学员和试卷信息进行排序;同时,还需处理异常情况;最后,需要自行编写测试案例以验证程序的正确性。
对题目的处理:
while (!(input = sc.nextLine()).equals("end")) {
String[] parts = input.split(":", 2);
switch (parts[0]) {
case "#N":
handleQuestion(parts[1], questions);
break;
case "#T":
handleTest(parts[1], questions, tests);
break;
case "#X":
handleStudent(parts[1], students);
break;
case "#S":
handleAnswerSession(parts[1], tests, students);
break;
case "#D":
handleDeleteQuestion(parts[1], questions);
break;
case "#Z": // Handle multiple choice questions
handleMCQuestion(parts[1], questions);
break;
case "#K": // Handle fill-in-the-blank questions
handleFIBQuestion(parts[1], questions);
break;
default:
System.out.println("wrong format:" + input);
break;
}
设计类图:
时序图:
SourceMonitor生成报表:
题目集5:
家居强电电路模拟程序-1:
题目分析:
本题目要求设计一个智能家居模拟系统,旨在通过模拟电路的方式,实现对家庭设备的智能控制。该系统包括控制设备(开关、分档调速器、连续调速器)和受控设备(白炽灯、日光灯、吊扇)。控制设备通过模拟电路中的开关和调速器来控制电压输出,进而影响受控设备的工作状态。
开关设备有两种状态,开(0)和关(1),控制电流的通断。分档调速器有多个档位,模拟中以4档调速器为例,输出电压为输入电压的0、0.3、0.6、0.9倍。连续调速器则无固定档位,输出电压为输入电压与档位参数的乘积,参数范围在0.00到1.00之间。
受控设备中,白炽灯的亮度与电位差成正比,而日光灯则只有亮和灭两种状态。吊扇的转速与工作电压区间成正比,电压区间为80V-150V,转速区间为80-360转/分钟。
系统输入包括设备信息、连接信息和控制设备调节信息。设备信息通过标识符和编号表示,连接信息描述设备引脚间的连接关系,调节信息则用于改变控制设备的状态或档位。电源接地标识为VCC(220V)和GND(0V),未接线的引脚默认接地。
输出信息要求按设备类型和编号顺序输出,显示设备的状态或参数。开关状态用“turned on”和“closed”表示,连续调速器的档位信息保留两位小数。系统在计算过程中使用double类型以保持精度,并在输出时按截尾规则处理小数。
根据题目设计了11个类:
主类Main:
读取输入直到遇到"end"。
解析输入信息,包括设备连接信息和控制设备调节信息。
创建设备实例,包括开关(Switch)、分档调速器(SteppedSpeedController)、连续调速器(ContinuousSpeedController)、白炽灯(IncandescentLamp)、日光灯(FluorescentLamp)和吊扇(CeilingFan)。
构建串联电路,将设备添加到SerialCircuit对象中。
更新设备状态,模拟电路的运行。
输出结果,按照设备类型和ID排序输出设备状态。
电路设备类CircuitDevice:
抽象类,定义了电路设备的基本属性和方法,如输入电压、输出电压、更新状态和获取状态。
作为所有电路设备的基类,定义了电路设备的基本属性和方法,如inputVoltage(输入电压)、outputVoltage(输出电压)、setInput(设置输入电压)、getOutput(获取输出电压)、updateState(更新状态)和getState(获取状态)。
控制设备类ControlDevice:
继承自CircuitDevice,代表可以控制的设备。
开关类Switch:
继承自ControlDevice,代表一个开关设备,可以开启或关闭。
分档调速器类SteppedSpeedController:
继承自ControlDevice,代表一个分档调速器,可以调整设备的档位。
连续调速器类ContinuousSpeedController:
继承自ControlDevice,代表一个连续调速器,可以设置设备的连续速度。
受控设备类ControlledDevice:
继承自CircuitDevice,代表受控设备,如灯具和风扇。
白炽灯类IncandescentLamp:
继承自ControlledDevice,代表一个白炽灯,根据电压差计算亮度。
日光灯类FluorescentLamp:
继承自ControlledDevice,代表一个日光灯,根据电压差计算亮度。
吊扇类CeilingFan:
继承自ControlledDevice,代表一个吊扇,根据电压差计算转速。
串联电路类SerialCircuit:
代表一个串联电路,包含多个设备,可以设置输入电压并更新设备状态。
设计逻辑如下:
1.程序首先通过Scanner读取用户输入,直到遇到"end"为止。这个过程中,它会解析两种类型的输入:设备间的连接信息和控制设备调节信息。连接信息用于记录哪些设备是相连的,而控制信息则用于改变设备的状态,比如开关设备的开闭状态、调速器的档位等。
2.接着,程序会根据连接信息创建设备实例。每种设备类型(如开关、调速器、灯具、风扇)都有对应的类,程序会根据设备ID的前缀来判断应该创建哪种类型的设备实例,并将这些实例存储在一个HashMap中。
3.然后,程序会构建串联电路。它会遍历连接信息,将相连的设备添加到同一个SerialCircuit实例中。每个SerialCircuit实例代表一个电路,包含一系列的设备。如果两个设备之间有连接,则它们会被添加到同一个电路中。
4.电路构建完成后,程序会更新每个电路的状态。它会为每个电路设置输入电压,并调用每个设备上的updateState方法来更新设备的状态。这个方法会根据设备的类型和输入电压来计算设备的输出电压或其他状态。
5.最后,程序会按照一定的顺序(开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇)输出每个设备的状态。这个顺序由一个字符串列表outputOrder定义,程序会根据这个顺序遍历设备,并打印出每个设备的状态。
设计类图:
时序图:
SourceMonitor生成报表:
题目集6:
家居强电电路模拟程序-2:
题目分析:这次作业在上一次基础上新增了落地扇作为受控设备,这意味着模拟系统现在需要处理两种风扇设备(吊扇和落地扇),每种都有其特定的工作电压区间和转速对应关系。对于白炽灯和日光灯,新引入了电阻的概念,这影响了电路中电流的计算,因为根据欧姆定律,电压、电流和电阻之间存在直接关系;支持并联电路,这使得电路设计可以更加灵活,模拟真实的家居环境中多种设备并行工作的情况,并联电路的引入也意味着需要处理多个电路路径和更复杂的电路拓扑结构。对串联电路和并联电路的信息进行了详细的规定,包括如何标识电路编号、如何描述电路的连接信息等。引入了“#T”和“#M”前缀来区分串联电路和并联电路的信息,使得输入信息的结构更加清晰。明确了电路连接的顺序和规则,比如串联电路的IN和OUT标识,以及并联电路的组成方式。同时也增加了对电路短路的合理性考虑,即在不损坏电路的情况下,短路是被允许的。由于设备种类的增加,输出信息需要包含新增设备的状态或参数,如落地扇的转速。
根据题目设计了12个类:
CircuitDevice(电路设备类):
这是一个抽象类,作为所有电路设备的基类。
包含两个受保护的成员变量:id(设备标识符)和inputVoltage(输入电压)。
提供了一个构造函数,用于初始化设备ID。
提供了setInput方法用于设置输入电压。
提供了getOutput方法用于获取输出电压。
包含两个抽象方法updateState和getState,这些方法需要在子类中具体实现。
ControlDevice(控制设备类):
继承自CircuitDevice,也是一个抽象类。
包含一个构造函数,调用父类的构造函数。
重写了updateState方法,但具体实现留给子类。
Switch(开关类):
继承自ControlDevice。
包含一个私有成员变量state,表示开关的状态(0或1)。
实现了updateState和getState方法,根据状态更新输出电压和返回状态描述。
SteppedSpeedController(分档调速器类):
继承自ControlDevice。
包含一个私有成员变量level,表示调速器的档位。
实现了updateState和getState方法,根据档位更新输出电压和返回档位描述。
提供了incrementLevel和decrementLevel方法用于调整档位。
ContinuousSpeedController(连续调速器类):
继承自ControlDevice。
包含一个私有成员变量level,表示调速器的档位参数。
实现了updateState和getState方法,根据档位参数更新输出电压和返回档位描述。
提供了setLevel方法用于设置档位参数。
ControlledDevice(受控设备类):
继承自CircuitDevice。
包含一个受保护的成员变量resistance,表示设备的电阻。
包含一个构造函数,用于初始化设备ID和电阻。
重写了setInput方法,根据输入电压和电阻计算输出电压。
IncandescentLamp(白炽灯类):
继承自ControlledDevice。
包含一个私有成员变量brightness,表示灯的亮度。
实现了updateState和getState方法,根据输出电压更新亮度和返回亮度值。
FluorescentLamp(日光灯类):
继承自ControlledDevice。
包含一个私有成员变量brightness,表示灯的亮度。
实现了updateState和getState方法,根据输出电压更新亮度和返回亮度值。
CeilingFan(吊扇类):
继承自ControlledDevice。
包含一个私有成员变量speed,表示风扇的转速。
实现了updateState和getState方法,根据输出电压更新转速和返回转速值。
FloorFan(落地扇类):
继承自ControlledDevice。
包含一个私有成员变量speed,表示风扇的转速。
实现了updateState和getState方法,根据输出电压更新转速和返回转速值。
SerialCircuit(串联电路类):
继承自CircuitDevice。
包含一个成员变量devices,表示串联电路中的设备列表。
提供了addDevice方法用于添加设备到电路中。
重写了setInput方法,用于设置输入电压并更新电路中每个设备的状态。
提供了getState方法,返回电路中所有设备的状态描述。
Main(主类):
包含main方法,是程序的入口点。
使用Scanner读取输入,解析设备连接信息和控制设备调节信息。
更新设备状态并输出结果。
设计类图:
时序图:
SourceMonitor生成报表:
踩坑心得
1.在使用正则表达式时,必须遵循更严格的标准。如果忽略了任何潜在的错误,不仅会导致当前问题的解答出错,还可能干扰后续的数据分割和输入工作,引发多种错误。这种情况会使得诊断问题根源变得更加复杂。
2.关于电路部分的计算不够熟练,并联电路的短路等很多情况没有考虑到,计算电阻的方法也有待改进。
改进建议
1.Main 类中直接进行了大量的逻辑操作,这违反了单一职责原则。Main 类应该只负责启动程序,而不是包含所有的业务逻辑,这样Main类不会过于冗长。
2.随着程序规模的增长,代码优化也变得尤为重要。需要我学会了如何通过设计模式、算法优化等手段来提升程序的性能和可读性。
总结
Java作为一种面向对象的编程语言,其核心优势在于封装、继承和多态。这几次作业充分体现了这一点,它让我们在编程过程中更加高效,减少了程序中的错误。继承作为一种强大的特性,它通过允许子类继承父类的属性和方法,促进了代码的复用,增强了代码的可维护性和可扩展性,同时也降低了开发的时间与成本。继承帮助我们建立了清晰的类层次结构,使得代码更加有序和易于理解,其中父类定义了通用的功能,而子类则在此基础上进行特定的扩展。此外,继承与多态性相结合,使得我们能够通过父类引用来调用子类的方法,这不仅实现了接口的统一性,还允许我们在运行时根据需要选择具体的实现。在需要对现有类进行扩展或修改时,继承使得我们可以轻松地添加新功能或调整现有功能,而无需改动父类的代码,这符合了开放封闭原则。继承还改善了代码的组织和管理,通过将共享的属性和方法集中在父类中,使得代码更加结构化和易于阅读,这对于团队协作和代码维护来说是非常有益的。
面向对象编程其实要更专注于一类东西上方面,在编程的时候思维不能局限于自己只是去解决一个实际问题,而是将其拓展开来,每一个细节都需要仔细考虑,编程的时候思维也要更加抽象,具有大局观,每一个决策都可能影响到程序的最终性能和可维护性。感谢在这次的设计和实现过程中,对我有过帮助的同学。在遇到难题时,讨论和交流往往能够带来新的视角和思路,让我学习到更多的知识和技能。