题目集4~6的总结性Blog
前言:
本次的三个作业是答题判题程序- 4、家居强电电路模拟程序- 1、家居强电电路模拟程序 -2的总结性blog。
一:答题判题程序- 4:
1. 知识点 类的封装、继承和多态实现功能,集合类的使用,正则表达式,异常处理,排序与数据管理
2. 题量与难度 这段代码涉及到的题目类型主要有三种:选择题、填空题。题目数量由输入的数据控制,程序需要处理多个题目、试卷、学生和答卷的情况。
3.难度: 代码的难度较高,特别是在处理面向对象设计时,涉及到的继承、多态和接口的使用需要一定的基础。 正则表达式的使用对于没有经验的开发者可能会稍显困难,因为需要理解正则表达式的语法和其在数据提取中的应用。 集合类的运用以及异常处理等也需要较好的理解。
二:家居强电电路模拟程序-1:
知识点: 继承和多态,抽象类和方法,集合框架,使用了Map(HashMap)存储设备,List(ArrayList)存储输入输出,Set(HashSet)存储设备ID等,展示了如何利用集合类来管理数据, 字符串处理。
题量: 设备管理,控制命令解析与处理,状态获取和排序
难度: 设备的状态管理和输入解析具有一定的复杂度,需要确保所有设备的状态正确同步。 设计并维护多个设备的状态,尤其是如何通过不同类型的设备互相影响(如开关影响电压,控制器影响电压等)可能让人感到较为复杂。 使用集合框架来存储和管理设备,确保代码能够有效地解析输入和输出,并正确地控制每个设备的行为。
三:家居强电电路模拟程序 -2:
在前一题的基础上增加了并联的内容,在逻辑处理上更加的复杂了。
设计与分析 :
答题判题程序- 4:
类和对象的设计: Exam:表示考试,包含题目管理功能。
AssessmentItem:表示一个评估项,即一道题目(可以是单选题、多选题或填空题)。
SingleChoiceQuestion, MultipleChoiceQuestion, FillInTheBlankQuestion:继承自 AssessmentItem,分别表示单项选择题、多项选择题和填空题。
QuestionScore:表示一道题的分数。
AnswerSheet:表示一个学生的答卷,包含题目答案及评分。
Student:表示一个学生的信息。
每个类的职责比较清晰,AssessmentItem 类是所有题目的基类,而单选、多选、填空题等都继承自这个基类,实现不同题型的评分方法。
AnswerSheet 负责存储学生的答案及计算分数。Exam 负责管理题目和试卷信息,Student 则存储学生的基本信息。
主要方法与实现: readExamData 方法: 该方法用于解析输入的数据,根据不同的输入格式调用相应的解析方法。通过正则表达式将每一行的输入拆解,并根据题目类型、试卷、学生信息等进行处理。
题目解析: 通过正则表达式解析不同类型的题目(如单项选择题、多项选择题、填空题),并将题目对象添加到 Exam 对象中。 解析后的题目会保存在 Exam 对象的 questions 字典中,以题号为键,题目对象为值。
试卷解析: 将试卷编号和对应题目的分数解析出来,并生成 QuestionScore 对象存储到试卷映射中。 每份试卷的题目和分数是通过一个列表存储的,后续会根据这个信息为学生评分。 答卷解析: 解析每个学生的答卷,将答案与试卷编号进行对应,并保存在 AnswerSheet 对象中。 每个答卷包含了该试卷的所有题目答案,之后可以进行评分和评判。
成绩输出: 在 displayResults 方法中,所有学生的答卷将按试卷编号排序后输出,输出每道题的答题情况(正确、部分正确、不正确)以及总得分。
在 Exam 类中,我们使用一个字典或列表来管理题目。题目可以分为不同的类型,如单选题、多选题、填空题等。每种题目都需要被解析并根据其类型生成相应的对象。
例如:
Map<Integer, AssessmentItem> questions = new HashMap<>();
在读取题目时,我们首先根据题目类型解析输入数据(例如单选、多选、填空),然后根据类型创建不同的 AssessmentItem 子类对象,如 SingleChoiceQuestion 或 MultipleChoiceQuestion,并将这些题目保存在字典中,方便后续使用。
2.2 试卷管理
每份试卷有自己的题目和对应的分数。试卷信息的存储可以使用一个数据结构来映射试卷编号和题目分数。通常,我们会使用一个映射表,存储每个试卷包含的题目及其对应分数。
例如:
Map<Integer, List<QuestionScore>> examPapers = new HashMap<>();
读取试卷数据时,我们根据输入的数据(例如 试卷编号、题目编号、分数)将信息解析并添加到 examPapers 中。
2.3 学生答卷管理
每个学生的答卷需要与其所参加的试卷对应,每个学生可以有一个或多个答卷。AnswerSheet 类存储了学生对每道题的答案以及相应的评分机制。
在答卷解析时,首先解析学生的学号、答题内容等,然后将这些答案与试卷信息关联,保存在 AnswerSheet 中。
例如:
Map<Integer, AnswerSheet> studentAnswers = new HashMap<>();
2.4 评分系统
评分系统是整个考试系统的核心,负责根据学生的答卷评判其正确性,并计算每道题的得分。评分的逻辑依据题目的类型来判断。
例如:
对于单选题,只需要判断学生选的答案是否与正确答案一致。
对于多选题,可能需要判断学生是否选对了所有正确选项,并计算部分得分。
对于填空题,可能需要比较学生的答案与标准答案是否匹配。
public void scoreAnswer(AnswerSheet answerSheet) {
for (AssessmentItem item : questions.values()) {
if (item instanceof SingleChoiceQuestion) {
// 判断学生选择的答案是否正确
} else if (item instanceof MultipleChoiceQuestion) {
// 判断学生选择的多个答案是否正确
} else if (item instanceof FillInTheBlankQuestion) {
// 判断学生填写的答案是否匹配
}
}
}
2.5 结果输出
最终的结果需要输出每个学生的得分及答题情况。我们会根据学生的 AnswerSheet,检查每道题的答案是否正确,显示题目、答案、是否正确以及得分。
例如:
public void displayResults() {
for (Student student : students) {
System.out.println("Student: " + student.getName());
AnswerSheet answerSheet = studentAnswers.get(student.getId());
for (AssessmentItem item : questions.values()) {
System.out.println(item.getQuestionText());
System.out.println("Answer: " + answerSheet.getAnswer(item.getId()));
System.out.println("Score: " + answerSheet.getScore(item.getId()));
}
}
}
2.6 题目的增删改
除了处理学生和试卷的管理,还需要支持题目的增删。通过输入命令(如 #D:N- 删除题目),系统会动态删除对应的题目并更新试卷信息。
删除题目时,需要在 Exam 类中删除该题,并更新所有试卷和答卷中相关题目的信息
家居强电电路模拟程序- 1:
类与对象设计
核心抽象类 CircuitDevice
CircuitDevice 是一个抽象类,作为所有设备类的父类。它定义了设备的一些通用属性和方法:
属性:
deviceType:设备类型(例如,开关、风扇、灯等)。
deviceId:设备的唯一标识符。
inputs 和 outputs:分别代表设备的输入和输出引脚。
构造函数:
初始化设备类型、设备ID,以及输入输出引脚的集合。
方法:
connect():连接两个设备的引脚,将输入引脚连接到输出引脚。
getStatus():这是一个抽象方法,每个设备类必须实现此方法来返回设备的当前状态。
通过该抽象类,所有设备类都可以继承并共享设备类型、ID和连接的基础逻辑,而不需要重复定义这些属性。
具体设备类
Switch:表示一个开关设备,包含 state 属性来记录开关的状态(0 表示打开,1 表示关闭)。toggle() 方法用来切换开关的状态。getStatus() 返回当前开关的状态。
DividedSpeedController:表示一个分档速度控制器,包含 position 属性来表示控制器的档位(0 至 3)。该类有 increase() 和 decrease() 方法来调整档位,outputVoltage() 方法返回根据档位计算出的输出电压。getStatus() 返回当前档位的状态。
ContinuousSpeedController:表示一个连续速度控制器,position 属性代表档位的电压输出(在 0 到 1 之间)。update() 方法用于更新档位,outputVoltage() 方法返回输出电压,getStatus() 返回当前档位的电压值。
FluorescentLamp:表示荧光灯,brightness 属性代表亮度。根据输入电压,inputVoltage() 方法更新亮度,getStatus() 返回亮度值。
IncandescentLamp:表示白炽灯,brightness 属性同样表示亮度。该类根据输入电压线性计算亮度,inputVoltage() 根据电压范围调整亮度,getStatus() 返回亮度值。
Fan:表示风扇,speed 属性表示风扇速度。根据输入电压,inputVoltage() 方法更新风扇速度,getStatus() 返回风速。
2. 设备管理与操作
SmartHomeCircuit 类
属性:
devices:一个 Map<String, CircuitDevice>,用来存储设备。设备的 deviceId 作为键,CircuitDevice 作为值,便于通过设备ID查找设备。
方法:
addDevice(CircuitDevice device):将设备添加到 devices 集合中。
processControl(String control):解析控制命令并根据命令更新设备状态。具体的命令格式包括开关操作、速度控制器的增减、连续控制器的电压更新等。
parseControlledDeviceInput():根据设备的当前状态计算控制电压,并将电压应用于所有需要的设备。主要根据开关的状态、电压控制器的输出等来更新设备的行为。
getDeviceStatus():按设备ID排序后打印所有设备的状态。
Main 类
输入读取与处理:使用 Scanner 类从标准输入读取数据,首先解析设备信息,将设备ID存入 deviceSet 中,然后读取控制命令并存入 operationList 中。遇到 "end" 时停止读取。
电路初始化:根据 deviceSet 中的设备ID,创建对应类型的设备实例,并将其添加到 SmartHomeCircuit 中。
控制命令处理:遍历 operationList,逐个处理控制命令,调用 SmartHomeCircuit 的 processControl 方法来更新设备状态。
状态更新:调用 parseControlledDeviceInput() 方法计算并更新电路中的设备状态,最后使用 getDeviceStatus() 方法按顺序输出所有设备的状态。
3. 控制命令与设备状态
控制命令:
K:表示开关操作,例如 K1 表示切换设备ID为1的开关状态。
F:表示分档速度控制器的增减操作,例如 F2+ 表示增加设备ID为2的分档控制器的档位。
L:表示连续速度控制器的电压更新,例如 L3:0.5 表示将设备ID为3的连续控制器的电压更新为0.5。
设备状态更新:
开关设备:根据控制命令切换开关状态,影响电路中的电压。如果开关设备状态为“打开”,会导致电压为0,从而影响后续设备的工作状态。
速度控制器:根据档位或者电压调整设备的输出电压,影响后续设备(如灯具和风扇)的行为。
灯具与风扇:灯具和风扇根据输入电压或速度值调整其状态(亮度或风速)。
1. 系统初始化与设备定义
1.1 设备与电路初始化
首先,系统通过 Scanner 类从标准输入读取设备和控制命令的相关信息。输入包括设备信息和操作命令:
设备信息:以 [] 包裹的设备ID,例如 [K1 K2 F1 L3 B4 R2],表示一个电路中有设备K1、K2(开关)、F1(分档控制器)、L3(连续控制器)、B4(白炽灯)、R2(荧光灯)等。
操作命令:以 # 开头的控制命令,例如 #K1、#F1+,表示设备的状态操作。命令格式由 K(开关控制)、F(分档速度控制器)、L(连续速度控制器)等组成。
系统通过 SmartHomeCircuit 类管理所有设备。在接收到设备信息后,系统根据设备ID的前缀(例如 K、F、L 等)实例化对应类型的设备,并将其加入到 SmartHomeCircuit 的设备列表中。
2. 控制命令的处理
2.1 解析控制命令
控制命令主要包含以下几种类型:
开关设备命令(K 开头):例如 #K1 表示切换设备 K1(开关)的状态。开关设备的状态在 Switch 类中用 state 属性表示,state = 0 表示打开,state = 1 表示关闭。当开关被切换时,电路的电压将受到影响。
分档速度控制器命令(F 开头):例如 #F1+ 表示增加设备 F1(分档控制器)的档位。分档速度控制器有 4 个档位(0 到 3),根据档位不同,其输出电压也会不同。命令 F1+ 会将设备 F1 的档位增加 1,而 F1- 会将档位减少 1。
连续控制器命令(L 开头):例如 #L3:0.5 表示设置设备 L3(连续控制器)的电压输出为 0.5。连续控制器的电压值是一个在 0 到 1 之间的浮动值。
2.2 执行命令
系统根据命令的类型找到对应设备,然后执行相关操作:
对于开关设备,通过 Switch 类的 toggle() 方法切换开关的状态。
对于分档速度控制器,通过 DividedSpeedController 类的 increase() 和 decrease() 方法调整档位。
对于连续速度控制器,通过 ContinuousSpeedController 类的 update() 方法更新电压值。
3. 电路中电压的计算与控制
3.1 控制电压的传递
系统根据当前设备的状态计算控制电压,并将其应用到需要电压输入的设备(例如灯、风扇)。电压的计算和传递通过以下规则进行:
开关设备:
如果开关设备的状态为 "打开" (state = 0),电路中的电压会被切断,即为 0V。
如果开关设备的状态为 "关闭" (state = 1),电压传递到后续设备。
速度控制器:
分档速度控制器(DividedSpeedController)根据当前的档位设置不同的电压输出。例如,档位为 0 时输出电压为 0V,档位为 1 时输出 66V,以此类推。
连续速度控制器(ContinuousSpeedController)根据设定的电压值(在 0 到 1 之间)输出对应的电压(例如 0.5 表示 110V)。
其他设备(如灯具、风扇):
灯具(如 FluorescentLamp 和 IncandescentLamp)的亮度取决于输入电压。例如,荧光灯在 0V 时亮度为 0,在非 0V 时亮度为最大值;而白炽灯则根据电压值进行线性插值,调整亮度。
风扇的速度(Fan)根据输入电压来设置。电压较低时风速为 0,电压较高时风速达到最大值,之间通过线性插值进行计算。
3.2 应用电压
每次控制命令执行后,SmartHomeCircuit 会调用 parseControlledDeviceInput() 方法,遍历电路中的所有设备,计算并应用电压到所有受控设备上。这些设备包括灯具、风扇等,它们的状态(如亮度、风速等)会根据输入电压实时更新。
4. 设备状态的获取与输出
4.1 获取设备状态
系统通过 getDeviceStatus() 方法获取所有设备的状态,并按设备ID的顺序打印出设备的状态。设备状态的输出分为几个步骤:
对设备ID进行排序。
按类别(开关、控制器、灯具、风扇等)依次打印每种设备的状态。
每个设备调用自己的 getStatus() 方法,返回其当前状态,例如开关的状态(打开/关闭)、灯具的亮度值、风扇的速度等。
4.2 打印状态
所有设备的状态按照 deviceId 的字典顺序进行输出,并且不同类型的设备按类别依次输出。每个设备的状态通过 getStatus() 方法返回,具体内容依设备的类型不同而有所差异。
5. 代码执行流程总结
初始化阶段:
系统从标准输入读取设备信息和控制命令。
创建并初始化所有设备对象,将其加入到 SmartHomeCircuit 中。
控制命令阶段:
遍历并解析所有控制命令,执行相应操作(如切换开关、增加档位、设置电压等)。
更新设备的状态,并根据控制命令更新电路中的电压。
状态更新与输出阶段:
根据当前设备的状态,计算电压并应用到所有相关设备(灯具、风扇等)。
获取所有设备的状态,并按要求格式输出。
采坑心得:
答题判题程序- 4
1. 输入格式错误导致的解析失败
问题描述:在代码中需要解析从控制台输入的题目、答案和学生信息等。输入的格式不符合预期,导致程序无法正确解析或抛出异常。
踩坑心得:
在编写代码时,要确保输入格式规范,尤其是在处理复杂数据时。例如,选择题和填空题的输入格式不一致,容易导致解析错误。正则表达式和输入的匹配应该非常严格,确保每个部分都能正确提取。
解决方案:可以使用 try-catch 来捕获格式错误并给出友好的提示,防止程序因输入格式问题崩溃。
2. 正则表达式匹配错误
问题描述:在解析题目、答案和学生答卷时,正则表达式匹配出现问题。题目中包含了特殊字符(如括号、点号等),导致正则表达式匹配失败。
踩坑心得
正则表达式在匹配时非常灵活但也容易出错,尤其是对于复杂的输入格式。编写正则时要特别小心转义字符和边界条件。
解决方案:可以在写正则时进行单元测试,逐步检查每个正则表达式的匹配效果。同时,对于复杂的正则,可以将其分解成多个小的正则进行逐一验证。
3. 数据结构选择不当
问题描述:在处理题目和答卷时,选择不当的数据结构,导致效率问题或程序无法正确处理数据。例如,使用 ArrayList 来存储学生答卷而不是 Map,可能会导致无法快速查找某个学生的答案。
踩坑心得:
在选择数据结构时,要考虑程序的访问效率。如果需要频繁进行查找操作,可以考虑使用 HashMap 或 TreeMap。如果数据需要按照顺序排序,可以使用 ArrayList 或 LinkedList。
解决方案:在设计数据结构时,提前思考每个数据的存取需求。确保选用适合的集合类,以提高程序效率和可读性。
4.多选题、填空题判分逻辑问题
- 问题描述:
①如果多选题答卷答案与标准答案部分相同,且没有包含不包括在标准答案中的答案,就判定为部分正确,分数计算为该题分数的一半,多余小数直接舍去。
解决方法:
家居强电电路模拟程序- 1:
1. 类型转换异常
错误:在 SmartHomeCircuit 类中的 processControl 方法中,使用了强制类型转换,如 Switch switchDevice = (Switch) devices.get(deviceId)。如果设备ID对应的设备不是 Switch 类型,则会抛出 ClassCastException。
编译结果:在运行时会发生 ClassCastException 错误,导致程序异常退出。具体错误信息如下:
解决办法:可以在执行强制类型转换之前,先使用 instanceof 检查设备类型。例如:
2. 类型匹配错误
错误:如果设备输入命令格式不正确,例如 #F1+ 操作在没有 F1 设备的情况下执行,或者 #L3:0.5 中的电压值格式不正确,会导致类型不匹配的错误。
编译结果:在编译时不会报错,但会在运行时遇到 NumberFormatException 或 IndexOutOfBoundsException 等异常。
解决办法:在解析输入命令时,进行格式校验和错误处理。例如,在解析数字时使用 try-catch 块来捕获异常:
3.未正确处理开关的状态对电压传播的影响
假设电路为:VCC -> 白炽灯 -> 开关。
如果开关关闭,白炽灯的状态不应受电压影响,但在设计中可能直接将 VCC 的电压传递给白炽灯,忽略了开关状态。
开关的状态应决定电压是否能继续传递到后续设备。
问题分析:
忽视开关的断路行为:开关断开时,应阻断电压传播,但可能在代码中直接递归调用后续设备的 setInputVoltage 方法。
开关行为未被单独抽象:开关在电路中是特殊的设备,既要管理自己的状态(开/关),又要影响电压的传播。
改进方案:
引入开关逻辑:开关应判断状态(开/关),仅在开启状态下向下传递电压。
修改电压传播逻辑:在 Switch.updateState() 方法中,控制是否调用后续设备的 setInputVoltage 方法。
解决方法:
改进建议:代码模块化
分解复杂的方法:如果某个方法非常长或复杂,应该拆分成多个较小的方法,每个方法只负责单一的功能。这样做不仅使代码更容易理解,也便于测试和调试。
遵循单一职责原则:每个方法或者类应该只承担单一的职责,避免类或者方法过于臃肿。
设备状态管理优化
在当前的 getDeviceStatus() 方法中,通过分别迭代设备并按顺序输出状态。这种方法较为繁琐,不易于扩展。如果以后要增加新的设备类型(例如空调、电视等),需要修改该方法。
改进方法:
可以使用反射机制,动态获取设备的类型并打印状态。或者在 CircuitDevice 类中添加一个 printStatus 方法,子类根据需求实现具体的输出逻辑。
电压传递逻辑的优化
当前 parseControlledDeviceInput 方法在解析电压时,首先检查了每个控制器的输出电压,并将其应用于所有设备。虽然这种实现方式简洁,但对于更复杂的电路(如串联、并联电路等),这种简单的传递电压的方式显得过于单一。
改进方法:
可以通过构建电路拓扑结构来模拟复杂的电路连接,如引入串联、并联等电路模型,从而更精确地计算电压和设备状态。
总结:
在这次实验中,我学习并实现了一个考试系统的核心逻辑,涉及了数据的输入、存储、处理和输出。通过设计并编写代码,我对以下几个方面有了深入的理解和掌握:
我加深了对面向对象编程的理解,学会了如何通过类来组织复杂的数据结构。
学会了如何利用继承、封装和多态等面向对象的原则,来让代码更加简洁、易扩展和可维护。
正则表达式:在处理输入数据时,使用了正则表达式来解析题目信息、试卷信息等。这让我对正则表达式的使用有了更深入的了解,能够高效地提取和验证文本中的关键信息。
数据结构:我使用了Map,学会了如何根据数据的特点选择合适的存储结构,并利用集合类来高效处理数据。
异常处理与输入验证:在解析输入数据时,我加入了异常处理机制,这让我学会了如何应对输入格式不符合预期的情况,并能给出相应的错误提示,保证程序的健壮性。
电路模拟的建模能力:通过模拟一个智能家居电路系统,我学到了如何将现实世界中的电气控制逻辑抽象为编程模型,理解了电压、状态、输入输出等电气概念如何在程序中体现。
控制命令的解析与设备状态管理:通过对不同控制命令(的解析与设备状态更新,我提高了对命令解析、状态管理的理解,尤其是在编写和管理交互式命令解析器方面的能力。
数据结构的应用:使用 Map 和 List 管理设备对象,以及通过 HashSet 去重设备ID,锻炼了对常见数据结构(如哈希表、列表、集合等)的实际应用能力。
需要进一步学习及研究的地方
正则表达式的深入应用:
虽然在本次实验中使用了正则表达式进行数据解析,但正则表达式的语法和应用还有很多可以进一步学习的地方,例如捕获组、反向引用等高级用法。
复杂数据结构的使用:
本实验中使用了HashMap等常见的数据结构,但对于一些更复杂的场景(例如图、树结构),我还需要进一步学习和掌握它们的应用和实现方式。
单元测试与调试技巧:
这次实验更多地侧重于代码实现和逻辑的构建,未来我需要进一步掌握如何为代码编写单元测试,并熟练使用调试工具进行调试,以提高开发效率和代码质量。
多线程与并发编程:
目前的程序是单线程运行的,对于大型考试系统或需要处理多个学生答题的场景,了解多线程和并发编程的知识将有助于提高系统的响应速度和处理能力。
课上实践与理论结合:
课堂上可以增加更多的实际编程练习,帮助学生加深对所学理论知识的理解。例如,在讲解数据结构或面向对象设计时,配合实际的代码示例和问题让学生更容易理解和掌握。
总结
通过这次实验,我不仅加深了对面向对象编程的理解,也学会了如何运用数据结构和正则表达式解决实际问题。在未来的学习中,我会继续深入学习这些技术,并加强调试和测试的能力。同时,我希望课程和实验能够结合实际问题,提供更多的实践机会和挑战,以便更好地提升学生的编程能力。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)