题目集4~6的总结

一:前言:

1.知识点:主要包括类和对象的使用、数据封装、方法的定义和使用、继承、多态、泛型、抽象类,集合框架,异常处理,字符串处理、以及基本的输入输出操作,每次题集的最后一题对于字符串的处理的要求都比较细致,有很多需要考虑的细节,这部分在后面详细介绍。
2.题量:现在的题目集都是一道题了,题量没什么说的。
3.难度:每次题集的难度都较大,每次都要7.8小时及以上,特别是第六次的电路题,大概写了10个小时,而且这次测试点还没提示了。第四次的题集主要是新增了单选题,多选题,填空题,难度适中,但是最后一个测试点对空格的要求太严格了,只删除末尾空格中的一个,很难想的到。第五次的题集是新开的电路模拟程序,由于是第一次需要考虑的东西比较少,只有串联和一个用电器,难度比较简单。第六次的电路模拟程序在第五次的基础上增加了并联,而且每个用电器都有电阻了,和我之前写的第五题的思路完全不同,需要改动的地方太多,相当于重写,而不是迭代,难度很大。

二:设计与分析:

第四次作业:
答题判题程序-4:本题要求实现一个的答题判题程序,包括题目的输入、试卷的输入,答案的提交、以及答案的校验和输出,在之前的基础上增加了单选题,多选题,填空题。
解题思路:设计了11个类:
Question 类
属性:
number:题目的编号。
content:题目内容。
standardAnswer:标准答案。
obtainedScore:获得的分数。
isDeleted:标记题目是否被删除。
方法:
构造函数:初始化题目的基本属性。
checkAnswer:检查给定的答案是否正确,并返回得分。
功能:表示一个题目,包含编号、内容、标准答案,并能检查给定的答案是否正确。

MultipleChoiceQuestion 类
继承:继承自Question类。
功能:处理多选题的特定逻辑,检查答案是否完全正确或部分正确。

FillInTheBlankQuestion 类
继承:继承自Question类。
功能:处理填空题的特定逻辑,检查答案是否完全正确或部分正确。

QuestionList 类
属性:
questions:存储题目的列表。
方法:
saveQuestion:保存题目到列表中。
getQuestion:根据题目编号获取题目。
功能:管理题目库,提供保存和获取题目的功能。

Exam 类
属性:
id:试卷的编号。
questionsExam:存储考试题目的列表。
totalScore:总分。
scores:存储每道题目的分数。
方法:
构造函数:初始化试卷的基本属性。
addQuestion:向考试中添加题目及其分数。
judgeAnswer:判断特定题目的答案是否正确,并返回得分。
功能:表示一次考试,包含多个题目,并能对答案进行校验。

ExamList 类
属性:
exams:存储考试的列表。
方法:
saveExam:保存考试到列表中。
getExam:根据试卷编号获取考试。
功能:管理考试列表,提供保存和获取考试的功能。

Student 类
属性:
id:学生编号。
name:学生姓名。
功能:表示学生信息。

StudentData 类
属性:
students:存储学生的列表。
方法:
saveStudent:保存学生到列表中。
getStudent:根据学生编号获取学生。
功能:管理学生信息库,提供保存和获取学生的功能。

AnswerPaper 类
属性:
paperId:答卷编号。
studentId:学生编号。
answers:存储题目答案的映射。
方法:
saveAnswer:保存题目答案。
getPaperId:获取答卷编号。
getStudentId:获取学生编号。
功能:表示一份答卷,包含答卷编号、学生编号和答案。

DeleteQues 类
属性:
Deles:存储被删除题目的编号列表。
方法:
saveDeletedQuestion:保存被删除题目的编号。
功能:管理需要删除的题目。

Main 类
方法:
main:接收命令行输入,初始化题目库、考试库、学生信息库、删除库和答卷库,读取数据,排序数据,输入数据,处理考试,并输出结果。
sortDataList:根据数据类型的预定义顺序对输入数据列表进行排序。
inputData:解析单条输入数据,并根据数据类型添加到相应的数据结构中。
processExams:处理所有答卷,计算分数,并输出每个学生的考试结果。
extractContent:从给定的字符串中提取特定模式的内容。
功能:
main 方法:程序的入口点,负责初始化数据结构,读取输入数据,调用数据处理和结果输出的方法。

数据流分析
输入数据:
数据源:程序通过 Scanner 对象从命令行输入读取数据。
数据类型:支持多种类型的输入数据,包括题目(#N:)、考试(#T:)、学生信息(#X:)、答卷(#S:)和删除题目(#D:)。
数据存储:所有输入数据行被存储在 List 类型的 dataList 集合中。

数据处理:
数据排序:使用 sortDataList 方法,根据数据行的前缀按照预定义的顺序(#N:, #K:, #Z:, #T:, #S:, #X:, #D:)进行排序,并将排序后的结果存储在 sortedList 中。
数据解析:遍历 sortedList,使用 inputData 方法解析每条数据行。根据数据行的前缀,调用 extractContent 方法提取相关信息,并将提取的数据封装到相应的类实例中:
Question:封装普通题目信息。
MultipleChoiceQuestion:封装多选题信息。
FillInTheBlankQuestion:封装填空题信息。
Exam:封装考试信息,包括题目编号和分数。
Student:封装学生信息。
AnswerPaper:封装学生答卷信息。
DeleteQues:封装需要删除的题目编号。

删除题目:
删除操作:inputData 方法中,如果数据行以 #D: 开头,表示需要删除题目。提取题目编号,并在 DeleteQues 的 Deles 列表中记录该编号。然后,在 QuestionList 中找到对应的 Question 对象,并将其 isDeleted 属性设置为 true。

分数判断:
总分检查:processExams 方法遍历 ExamList 中的所有 Exam 对象,检查每个考试的总分是否为100分。如果不是,输出警告信息。

答卷比较:
答卷处理:processExams 方法遍历 paperBank 中的所有 AnswerPaper 对象,对于每个学生的答卷:
获取对应的 Exam 对象。
遍历 Exam 对象中的题目列表,对于每个题目,使用 judgeAnswer 方法比较学生的答案与标准答案。
根据比较结果,计算每个题目的得分,并累加到总分中。
输出每个学生的答题情况和总分。

输出结果:
结果展示:对于每个学生的答卷,按照学生ID、姓名、每题得分和总分的格式输出结果。

类图
image

第五次作业:
家居强电电路模拟程序-1:模拟了一个电路系统中设备的操作,包括开关、调速器和受控设备(如灯具和风扇)
解题思路:设计了10个类:
Device 类(设备基类)
属性:
id:设备的编号。
pin1:输入引脚,表示输入电压。
pin2:输出引脚,表示输出电压。
方法:
构造函数:初始化设备的基本属性。
功能:作为所有设备的基类,定义了设备的共同属性。

ControlDevice 类(控制设备基类)
继承:继承自Device类。
方法:
OutVoltage(double v):抽象方法,返回设备的输出电压。
功能:作为所有控制设备的基类,定义了控制设备的共同行为。

ControlledDevice 类(受控设备基类)
继承:继承自Device类。
方法:
setState(double v):抽象方法,根据输入电压设置设备状态。
功能:作为所有受控设备的基类,定义了受控设备的共同行为。

Switch 类(开关设备)
继承:继承自ControlDevice类。
属性:
state:表示开关的当前状态。
方法:
OutVoltage(double v):返回开关的输出电压。
control():控制开关的状态。
getState():返回开关的当前状态。
功能:表示一个开关设备,能够控制电流的通断。

GearSpeedRegulator 类(齿轮速度调节器)
继承:继承自ControlDevice类。
属性:
flag:表示速度档位。
方法:
OutVoltage(double v):根据速度档位返回不同的输出电压。
功能:表示一个齿轮速度调节器,能够调节输出电压以控制速度。

ContinuousSpeedRegulator 类(连续速度调节器)
继承:继承自ControlDevice类。
属性:
parameter:表示速度参数。
方法:
OutVoltage(double v):根据速度参数返回调整后的输出电压。
功能:表示一个连续速度调节器,能够根据参数连续调节输出电压。

IncandescentLamp 类(白炽灯)
继承:继承自ControlledDevice类。
属性:
light:表示灯的亮度。
方法:
setState(double v):根据输入电压设置灯的亮度。
功能:表示一个白炽灯,能够根据电压调整亮度。

FluorescentLamp 类(荧光灯)
继承:继承自ControlledDevice类。
属性:
light:表示灯的亮度。
方法:
setState(double v):根据输入电压设置灯的亮度。
功能:表示一个荧光灯,能够根据电压调整亮度。

CeilingFan 类(吊扇)
继承:继承自ControlledDevice类。
属性:
speed:表示风扇的速度。
方法:
setState(double v):根据输入电压设置风扇的速度。
功能:表示一个吊扇,能够根据电压调整转速。

Main 类
方法:
main:程序的入口点,负责读取输入数据,创建设备,设置电压,重置电压和输出设备状态。
createDevice:根据输入数据创建设备实例。
SetPotential:设置设备的电压。
ResetPotential:重置设备的电压。
OutPrintf:输出所有设备的状态。
功能:负责整个程序的流程控制和数据处理。

数据流分析
输入数据:
程序通过 Scanner 对象从命令行输入读取数据,支持多种类型的输入数据,包括设备定义、控制命令和电压设置。
数据处理:
使用 createDevice 方法根据输入数据创建设备实例。
使用 SetPotential 方法根据电路连接设置设备的电压。
使用 ResetPotential 方法在所有开关关闭时重置受控设备的电压。
使用 OutPrintf 方法输出所有设备的状态。

类图
image

第六次作业:
家居强电电路模拟程序-2:模拟了一个电路系统中设备的操作,包括开关、调速器和受控设备(如灯具和风扇),在之前的基础上加了并联电路。
解题思路:设计了12个类:
Device 类(设备基类)
属性:
id:设备的编号。
pin1:输入引脚,表示输入电压。
pin2:输出引脚,表示输出电压。
vd:电压差。
resistance:总电阻。
方法:
构造函数:初始化设备的基本属性。
getResistance:抽象方法,获取总电阻。
功能:作为所有设备的基类,定义了设备的共同属性和行为。

ControlDevice 类(控制设备基类)
继承:继承自Device类。
方法:
OutVoltage(double v):抽象方法,根据输入电压得到输出电压。
getResistance:返回总电阻。
功能:作为所有控制设备的基类,定义了控制设备的共同行为。

ControlledDevice 类(受控设备基类)
继承:继承自Device类。
方法:
setState(double v):抽象方法,根据电压差设置状态参数。
功能:作为所有受控设备的基类,定义了受控设备的共同行为。

SerialCircuit 类(串联电路类)
属性:
devices:存储串联电路中的设备列表。
totalResistance:总电阻。
方法:
addDevice:添加设备到串联电路。
calculateVoltage:计算串联电路中每个设备的电压差。
功能:表示一个串联电路,管理电路中的设备和计算电压差。

ParallelCircuit 类(并联电路类)
属性:
circuits:存储并联电路中的串联电路列表。
totalResistance:总电阻。
方法:
addCircuit:添加串联电路到并联电路。
updateResistance:更新并联电路的总电阻。
calculateVoltage:计算并联电路中每个串联电路的电压差。
功能:表示一个并联电路,管理电路中的串联电路和计算电压差。

Switch 类(开关设备)
继承:继承自ControlDevice类。
属性:
state:表示开关的当前状态。
方法:
OutVoltage(double v):返回开关的输出电压。
control:控制开关的状态。
功能:表示一个开关设备,能够控制电流的通断。

GearSpeedRegulator 类(齿轮速度调节器)
继承:继承自ControlDevice类。
属性:
flag:表示速度档位。
方法:
changeGear:改变速度档位。
OutVoltage(double v):根据速度档位返回不同的输出电压。
功能:表示一个齿轮速度调节器,能够调节输出电压以控制速度。

ContinuousSpeedRegulator 类(连续速度调节器)
继承:继承自ControlDevice类。
属性:
parameter:表示速度参数。
方法:
OutVoltage(double v):根据速度参数返回调整后的输出电压。
功能:表示一个连续速度调节器,能够根据参数连续调节输出电压。

IncandescentLamp 类(白炽灯)
继承:继承自ControlledDevice类。
属性:
light:表示灯的亮度。
resistance:总电阻。
方法:
setState(double v):根据电压差设置灯的亮度。
getResistance:返回总电阻。
功能:表示一个白炽灯,能够根据电压调整亮度。

FluorescentLamp 类(荧光灯)
继承:继承自ControlledDevice类。
属性:
light:表示灯的亮度。
resistance:总电阻。
方法:
setState(double v):根据电压差设置灯的亮度。
getResistance:返回总电阻。
功能:表示一个荧光灯,能够根据电压调整亮度。

CeilingFan 类(吊扇)
继承:继承自ControlledDevice类。
属性:
speed:表示风扇的速度。
resistance:总电阻。
方法:
setState(double v):根据电压差设置风扇的速度。
getResistance:返回总电阻。
功能:表示一个吊扇,能够根据电压调整转速。

FloorFan 类(落地扇)
继承:继承自ControlledDevice类。
属性:
speed:表示风扇的速度。
resistance:总电阻。
方法:
setState(double v):根据电压差设置风扇的速度。
getResistance:返回总电阻。
功能:表示一个落地扇,能够根据电压调整转速。

Main 类
方法:
main:程序的入口点,负责读取输入数据,创建设备,设置电阻,计算电压,打印结果。
handleLines:处理输入的每一行数据。
setResistance:设置设备的电阻。
processCircuit:处理电路定义。
getOrCreateDevice:获取或创建设备。
printResults:打印结果。
功能:
main 方法:程序的入口点,负责初始化数据结构,读取输入数据,调用数据处理和结果输出的方法。

数据流分析
输入数据:
程序通过 Scanner 对象从命令行输入读取数据,支持多种类型的输入数据,包括设备定义、控制命令和电压设置。
数据处理:
使用 handleLines 方法处理输入的每一行数据,根据数据类型创建或更新设备状态。
使用 setResistance 方法设置设备的电阻。
使用 processCircuit 方法处理电路定义,创建串联和并联电路。
使用 getOrCreateDevice 方法获取或创建设备实例。
计算电压:
使用 calculateVoltage 方法计算每个电路的电压差。
输出结果:
使用 printResults 方法输出所有设备的状态。

类图:
image

三:采坑心得:

第四次作业:
1.在处理多选题时,我认为答案只能为A-Z,所用正则表达式去掉那些不符合这个格式的数据,导致错了前面的三个测试点

错误的正则表达式如下

点击查看代码
            String regex6 = "#Z:\\d\\s#Q:.+\\s#A:[\\sA-Z]*";
修改后的正则表达式如下
点击查看代码
            String regex6 = "#Z:.+#Q:.+#A:.+";
2.在处理填空题时,一开始认为只要答案中存在答案正确就判定为部分正确,其实为只要存在部分答案错误就算完全错误得零分。 思路:用正确答案和学生答案求交集,然后分情况判断,代码如下
点击查看代码

givenAnswerSet1.retainAll(correctAnswerSet1); // 保留交集
        if (givenAnswerSet1.size() == correctAnswerSet1.size() && givenAnswerSet1.size() == givenAnswers1.length) {
            return 1; // 完全正确
        }
        else if (givenAnswerSet1.size() > 0 && givenAnswerSet1.size() == givenAnswers1.length) {
            return 2; // 不包含错误答案且部分正确
        }
        else{
            return 0; // 错误
        }

3.没有正确处理末尾的空格,最后一个测试点一直为格式错误,在问其他同学后得知,最后一个测试点为若答案末尾有两个空格,只能去掉最后一个空格,但是在判断答案时去掉所有空格判断
代码如下:

点击查看代码
                    if(answer.charAt(answer.length()-1) == ' ') //去掉末尾的一个空格
                      answer = answer.substring(0, answer.length()-1);

第五次作业:
1.在遍历电路设置电位时,我一开始我只考虑到在用电器前面的开关,如果为打开,电位就设置为0,但是没有想到在用电器后面的开关,会影响前面的用电器的电位导致有几个测试点没过。
所以我在后面遍历所有开关,若有打开的则把所有的电位置0。
代码如下:

点击查看代码
    public static void ResetPotential(List<Switch> switches, List<IncandescentLamp> whites,
                                       List<FluorescentLamp> suns, List<CeilingFan> fans)         //重置电位
    {
        int flat = 0;
        for(Switch a : switches){
            if(!a.state){
                flat = 1;        //如果存在打开的开关
                break;
            }
        }
        if(flat == 1){    //把所有用电器的输入电位置0
            for(IncandescentLamp a : whites){
                a.pin1 = 0.0;
            }
            for(FluorescentLamp a : suns)
            {
                a.pin1 = 0.0;
            }
            for(CeilingFan a : fans){
                a.pin1 = 0.0;
            }
        }
    }

第六次作业:
这次作业虽然只是在上次的作业的基础上加入并联,但是感觉难度提升很大。我之前的思路是通过遍历电路,给每个设备的输入和输出引脚的电位赋值。
在经过许多思考和尝试后,认为这种方法在这题上面行不通,最后我是通过计算每个设备的总电阻,然后通过分压来直接计算每个设备的电压差。
1.一开始我的思路为先遍历所有的串联电路,然后再处理并联电路。这样会导致在处理总串联电路总算电阻时,将还没有计算的并联电路的电阻当成0,
加入计算到总电阻中导致了错误。
改进方法:后面我改成了先遍历子串联电路,判断每个子串联电路的状态,然后再计算总电阻,再遍历并联和总串联电路。

2.在根据子串联电路的状态来判断并联电路的状态时出错,然后导致判断总电路的状态错误,没有考虑清楚短路和断路的问题。
改进方法:先通过子串联电路的状态来设定并联电路的状态,最后判断总电路的状态。判断并联电路的状态时比较复杂,若子串联的状态全为断路则并联电路为断路,若存在子串联的状态有一个为短路,则并联电路为短路,其他为通路。
代码如下:

点击查看代码
//判断并联电路的状态
            boolean allZero = true; //全零为断路
            boolean hasNegativeOne = false; //存在一条路为短路
            for(SerialCircuit cir : parallelCircuit.circuits){
                int flat = cir.flat;
                if (flat != 0) {
                    allZero = false;
                }
                if (flat == -1) {
                    hasNegativeOne = true;
                }
                parallelCircuit.updateResistance();
            }
            if (allZero) {
                parallelCircuit.flat = 0;
                parallelCircuit.totalResistance = 0;
            } else if (hasNegativeOne) {
                parallelCircuit.flat = -1;
                parallelCircuit.totalResistance = 0;
            } else {
                parallelCircuit.flat = 1;
            }

四:改进建议:

第四次作业:

关键指标分析
语句数 (Statements): 247条语句,这个数量表明代码文件中包含了相当多的操作。
分支语句百分比 (Percent Branch Statements): 21.5%,这个比例表明代码中包含了较多的条件判断,这可能增加代码的复杂性。
方法调用语句 (Method Call Statements): 147,这表明代码中方法调用较为频繁,这通常是好的,但如果方法调用链过长,可能会影响性能。
注释率 (Percent Lines with Comments): 6.8%,注释率较低,这可能影响代码的可读性和可维护性。
类和接口数量 (Classes and Interfaces): 11,这个数量适中,但需要确保每个类和接口都有清晰的职责。

改进建议
重构代码: 考虑将Main.java文件中的代码重构为更小的模块,每个模块负责一个单一职责,以降低复杂度。
增加注释: 为代码添加更多的注释,特别是对于复杂的逻辑和方法,以提高代码的可读性。
简化复杂方法: 尝试简化最复杂的方法,可能通过提取子方法或重构逻辑来实现。

第五次作业:

关键指标分析
语句数 (Statements): 264条语句,这个数量表明代码文件中包含了相当多的操作。
分支语句百分比 (Percent Branch Statements): 33.7%,这个比例表明代码中包含了较多的条件判断,这可能增加代码的复杂性。
方法调用语句 (Method Call Statements): 126,这表明代码中方法调用较为频繁,这通常是好的,但如果方法调用链过长,可能会影响性能。
注释率 (Percent Lines with Comments): 2.0%,这个比例非常低,建议增加注释以提高代码的可读性和可维护性。
类和接口数量 (Classes and Interfaces): 8,这个数量适中,但需要确保每个类和接口都有清晰的职责。
每类方法数 (Methods per Class): 2.88,这个数字表明每个类中的方法数量不多,这有助于保持类的职责单一。
每方法平均语句数 (Average Statements per Method): 11.04,这个数字表明方法的平均长度,建议保持方法简短和专注。
最复杂方法行数 (Line Number of Most Complex Method): 218行,这表明至少有一个方法非常复杂,可能需要重构。
最复杂方法名称 (Name of Most Complex Method): Main.SetPotential(),这表明该方法可能是重构的候选。
最大复杂度 (Maximum Complexity): 45,这个复杂度相对较高,建议简化逻辑。
最深块行数 (Line Number of Deepest Block): 292,这可能意味着有深层嵌套的控制结构,这可能影响代码的可读性。
平均块深度 (Average Block Depth): 3.42,这个深度相对较低,表明代码的嵌套结构不深。
平均复杂度 (Average Complexity): 5.23,这个复杂度表明方法的平均复杂性,建议简化方法逻辑。

改进建议
重构复杂方法: 特别是Main.SetPotential(),考虑将其拆分为多个小方法。
增加注释: 提高代码的注释率,使其更易于理解和维护。
简化逻辑: 尝试简化复杂的条件逻辑,减少嵌套的深度。
使用设计模式: 考虑应用设计模式,如工厂模式来创建对象,或策略模式来封装算法的变化。
优化类结构: 确保每个类都有单一职责,避免类承担过多的功能。

第六次作业:

关键指标分析
圈复杂度(Maximum Complexity): 9 是一个较高的复杂度,表明 Main.main() 方法可能执行了过多的任务,违反了单一职责原则。
最复杂方法的行数(Line Number of Most Complex Method): 200 行,这表明该方法可能过于复杂,难以理解和测试。
方法调用语句(Method Call Statements): 134 表明代码中方法调用较为频繁,这可能是代码重用性高的一个好迹象,但也可能意味着方法之间的耦合度高。
注释率(Percent Lines with Comments): 6.5%,注释率较低,这可能影响代码的可读性和可维护性。
平均块深度(Average Block Depth): 1.71,这个值相对较低,表明代码的嵌套结构不深,这通常是好的。
类和接口数量(Classes and Interfaces): 12,这个数量适中,但需要确保每个类和接口都有清晰的职责。

改进建议
重构 Main.main() 方法:考虑将 Main.main() 方法拆分成多个小方法,每个方法负责一个单一职责,以降低复杂度。
增加注释:为代码添加更多的注释,特别是对于复杂的逻辑和方法,以提高代码的可读性。
简化复杂方法:尝试简化最复杂方法 Main.main(),可能通过提取子方法或重构逻辑来实现。

五:总结:

本阶段的三次题目集涵盖了Java编程的多个方面,包括面向对象编程、数据结构、异常处理、正则表达式、输入输出处理等。
通过这三次的题目,我学会了Java集合框架,了解它们的用途和性能特点,对字符串的处理,split,trim,substring,indexof等方法,学会了面向对象的思想,
接下来应该深入学习异常处理:学习如何自定义异常,并在代码中合理使用try-catch-finally块来处理异常,以及一些java模型,如装饰模型,桥接模型等,在写代码时可以启到事半功倍的效果,便于迭代。
学习心得:在写题时不能看个大概就直接开始写,应该在反复阅读题目设计好解题思路,画出简易的类图后开始写。
不然在后面调试代码改错时会花费大量时间
建议及意见:希望老师在上课时多讲一些后面大作业可能需要用到的语法和技巧,同时建议能够给出大作业中所有的测试点,有的时候一个测试点需要猜几个小时。

posted @   22207330-曾融  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示