南昌航空大学 22207209-侯智慧-第一次blog作业
一、前言
在过去的几个月中,我全身心地投入到了Javapta课程的学习中,尤其是三次极具挑战性的大作业。这些作业不仅是对我Java编程技能的考验,也是我在计算机科学领域不断探索和成长的重要里程碑。
最初接触这些作业时,我感到有些不知所措。每个项目都要求我们运用不同的Java特性,从基本的面向对象编程到复杂的数据结构和算法优化。面对这些挑战,我逐渐意识到,学习编程不仅仅是掌握语法和工具,更是培养一种解决问题的思维方式。
在项目进行过程中,我不断遇到各种技术难题和逻辑陷阱。有时候,一个小小的错误可能会花费数小时甚至数天去调试和修复。然而,正是在这些反复试错和不断迭代的过程中,我的编程能力得到了显著提升。我学会了如何更有效地调试代码,如何优化算法以提高程序效率,以及如何在团队协作中利用版本控制工具来管理项目。
此外,这些作业也让我深刻体会到理论与实践相结合的重要性。在课堂上学到的概念,只有通过实际编码和项目应用,才能真正内化为自己的技能。每次成功解决一个棘手的问题,或者在项目中实现一个新功能,都会让我感受到极大的成就感和满足感。
在这篇博客中,我将详细分享每个作业的背景、我所采用的解决方案,以及在过程中积累的经验和教训。我希望这些分享不仅能帮助其他正在学习Java的同学,也能为那些对编程充满好奇和热情的人提供一些有用的启示。编程之路漫漫,但每一步的努力和探索都值得我们珍惜和回味。让我们一起继续这段充满挑战的学习旅程吧!
二、设计与分析
1.第一次作业7-1:设计一个风扇Fan类
(1)难度分析:
- 这道题目属于入门级别,主要考察面向对象编程的基础知识,包括类的定义、构造方法的使用、数据封装、常量的定义以及方法的重写。对于初学者来说,这道题目是一个很好的练习机会,可以帮助他们理解和掌握Java类的基本结构和功能。
(2)知识点分析: - 常量的定义:
题目要求定义三个表示风扇速度的常量(SLOW、MEDIUM、FAST),这可以通过public static final关键字来实现。常量是不可变的,通常在类中定义为public和static。 - 私有数据域:
题目要求定义几个私有数据域,包括speed、on、radius和color。这些数据域需要通过构造方法进行初始化,并通过访问器(getter)和修改器(setter)进行访问和修改。 - 构造方法:
需要实现一个无参构造方法和一个有参构造方法。无参构造方法用于创建默认对象,而有参构造方法用于根据给定参数创建自定义对象。 - 访问器和修改器:
访问器和修改器是用于访问和修改私有数据域的方法。它们通常被命名为getX和setX,其中X是数据域的名称。 - toString方法的重写:
toString方法用于返回对象的字符串表示。需要根据风扇的状态(开或关)返回不同的字符串信息。 - 对象的创建和使用:
题目要求创建两个Fan对象,并通过调用toString方法显示对象的信息。这涉及到对象的实例化和方法调用。
(3)类图的设计
(4)踩坑心得 - 无
2.7-5 答题判题程序-1
(1)难度分析: - 这道题目属于中等难度,涉及到面向对象编程的多个方面,如类设计、集合操作、字符串处理和输入输出等。
(2)知识点分析: - 集合操作:
使用List集合来存储题目和答案,涉及到集合的添加、遍历、排序等操作。 - 字符串处理:
需要对输入的字符串进行解析和处理,提取题号、题目内容和答案等信息。这涉及到字符串分割、替换和修剪等操作。
(3)类图的设计
(4)踩坑心得及改进建议 - 输入过程中没有正确的去掉空格
在这段代码中正确的处理了去掉空格的操作,利用trim()函数去空格,如果不正确的去掉空格,输出中会有多余的空格,导致与输入中的标准答案不匹配,导致出错。
String[] parts = question.split("#"); // 使用 "#" 分隔每部分信息利用这段函数,将题目类的所有信息都分隔出来,以保证后续与答题类的匹配。 - 答题信息中没有正确的处理"#A:"+答案内容
正确的处理为:String[] userAnswers = input.nextLine().replaceAll("#A:", "").trim().split("\s+");
其中.replaceAll("#A:", ""),目的是为了使用正则表达式将字符串中的#A:部分替换为空字符串。这一步的目的是去掉输入中用于标识答案部分的前缀#A:,以便后续处理。
.split("\s+"),目的是为了使用正则表达式\s+来分割字符串。\s+表示匹配一个或多个空白字符(包括空格、制表符、换行符等),因此该方法会将输入字符串按空白字符分割成多个部分,
结果是一个字符串数组,其中每个元素对应一个用户提供的答案。
3.7-4 答题判题程序-2
(1)难度分析
这道题目属于较难题目 - 多种输入类型的处理:
输入信息包括题目信息、试卷信息和答卷信息,这些数据可能会混合输入,并且顺序不定,需要程序能够正确地解析和区分。
解析输入格式的复杂性增加了实现的难度,尤其是在处理格式不正确或不完整的输入时。 - 数据结构设计:
需要设计合适的数据结构来存储题目、试卷和答卷的信息,并且这些数据结构之间要有合理的关联。
例如,题目和试卷之间通过题号关联,试卷和答卷通过试卷号关联。 - 逻辑实现的复杂性:
需要实现对答案的判定,并计算每道题的得分以及总分。
还要处理特殊情况,例如试卷总分不为100的警示、答卷中多余或缺失的答案处理等。 - 输出格式的复杂性:
输出需要根据不同的条件给出不同的信息,这要求程序能够灵活地生成输出,并且格式必须严格符合要求。 - 错误处理:
需要处理各种可能的错误情况,例如输入格式错误、找不到试卷号等。
(2)知识点分析 - 字符串处理:
使用正则表达式和字符串操作方法(如split、trim、replaceAll等)来解析和处理输入字符串。 - 集合和映射:
使用集合(如List、Map)来存储和管理题目、试卷和答案信息。
(3)类图的设计
(4)踩坑心得及改进建议 - 出现所有测试点都是非零返回
问题出现在输入时
修改之前的代码中没有加上.trim()操作导致对于正则表达的拆解有问题,导致所有测试点非零返回。
.trim() 的作用是去除字符串两端的空格,可以处理输入数据中题号、内容或前后不小心包含空格的情况,避免因为这些额外的空格导致程序出错。 - 在输入的处理中错误的使用了空格分隔
String[] parts = line.split(" ");
错误地使用了空格分割 #N 行,而 #N 行的格式是用 # 分隔字段的。
String[] parts = line.split("#");
使用 # 分割,并添加了 .trim() 去除空格,确保题号、内容和标准答案解析正确。 - 出现单试卷单答卷满分不是100分的问题
是因为在输入函数InputHandler类中在创建试卷时,没有检查所选题目的分数总和是否等于 100 分,导致试卷满分可能不是 100 分。
修改意见: 在 InputHandler 类的 processQuestions 方法中,添加了对试卷总分的检查。在创建完试卷后,计算试卷的总分,并与 100 分进行比较。如果不等于 100 分,则将警报信息添加到 alerts 列表中。
if (paper.getTotalScore() != 100) {
alerts.add("alert: full score of test paper" + paperNumber + " is not 100 points");
} - 出现2试卷2答卷 满分不是100 答案有缺失 答卷试卷号不匹配的问题
出现问题的原因是因为学生提交的答卷可能与试卷不匹配,可能答卷中指定的试卷号不存在。
修改意见:当指定的试卷号不存在是,可以添加一个带有空试卷的Answer对象
修改后的代码:
TestPaper paper = testPapers.get(paperNumber);
if (paper == null) {
System.out.println("The test paper number does not exist"); // 明确提示试卷号不存在
// 将 Answer 对象添加到 allAnswers 列表中,即使试卷不存在,以便后续统一处理和输出结果
allAnswers.add(new Answer(null)); // 添加一个带有空试卷的 Answer 对象
return;
}
4.7-3 答题判题程序-3
(1)难度分析
这道题目是一道综合性较高的编程题,涉及到多个知识点和复杂的逻辑处理。 - 输入格式复杂:
题目要求处理多种不同格式的输入数据,包括题目信息、试卷信息、学生信息、答卷信息和删除题目信息。这些信息的格式各不相同,并且输入顺序可能是混乱的,这要求程序具有很强的解析能力。 - 数据关联性强:
不同类型的数据之间有着紧密的关联。例如,试卷信息中的题目编号需要与题目信息对应,答卷信息中的试卷号和学号需要与试卷信息和学生信息对应。这种关联性要求程序在解析和处理数据时保持一致性。 - 多种错误处理:
题目要求对多种错误情况进行处理,包括格式错误、题目引用错误、试卷号引用错误和学号引用错误。这需要程序能够检测并处理多种异常情况,并给出相应的提示信息。 - 多种输出格式:
根据不同的输入情况,程序需要输出不同的提示信息和结果,包括试卷总分警示、答卷信息、判分信息、被删除题目提示信息、题目引用错误提示信息等。这要求程序能够根据不同的逻辑分支生成多种格式的输出。
逻辑复杂:题目涉及到的逻辑判断较多,包括答案的正确性判断、题目的有效性判断、分数的计算等。这需要程序能够处理复杂的业务逻辑。
(2)知识点分析 - 类和对象:
代码定义了多个类:Question、Paper、Student、AnswerSheet、InPut、OutPut。这些类分别代表题目、试卷、学生、答题纸、输入处理和输出处理。
每个类都有自己的属性和方法,体现了面向对象编程中的封装性。 - 封装:
类的属性通常是私有的(private),通过公共方法(public)访问和修改这些属性。这种封装机制有助于保护数据不被外部直接修改。 - 内部类:
Paper类中定义了一个内部类QuestionScore,用于存储题目编号和分值。这种设计有助于将相关的逻辑和数据封装在一起。 - 列表和映射:
使用List(如ArrayList)来存储题目、试卷、学生和答题纸的列表。
使用Map(如HashMap)来存储答案列表,题目顺序号对应答案。这些数据结构有助于快速查找和存储数据。 - 集合:
使用Set(如HashSet)来记录被删除的题目编号,确保题目编号的唯一性。 - 字符串分割和解析:
使用String.split()方法对输入字符串进行分割,提取出题号、题目内容、标准答案等信息。
使用正则表达式来验证输入格式的正确性。 - 字符串比较和修剪:
使用String.trim()方法去除字符串首尾的空格。
使用String.equals()方法比较字符串内容。 - 条件判断:
使用if语句进行条件判断,验证输入的正确性,并根据不同情况执行不同的操作。 - 错误提示:
检测到格式错误时,输出错误提示信息。 - 题目和答案的关联:
通过题号和顺序号将题目与答案关联起来,判断答案的正确性并计算得分。 - 题目删除和失效处理:
实现了题目删除功能,被删除的题目在答卷中显示为失效,并且得分为0。 - 试卷总分警示:
检查试卷的总分是否为100分,并在不满足条件时给出警示。 - 无效试卷和学生处理:
检查试卷号和学号的有效性,并在不匹配时输出相应的提示信息。
(3)类图的设计
(4)踩坑心得及改进建议 - 在有多个题目的时候,没有计算每一道题的得分,例如样例三:
N:1 #Q:1+1= #A:2
N:2 #Q:2+2= #A:4
T:1 1-5 2-8
X:20201103 Tom-20201104 Jack-20201105 Www
S:1 20201103 #A:1-5 #A:2-4
D:N-2
end
在输出得分的时候,错误输出了:20201103 Tom:0~0,只有一道题的分数,而且我在代码中并没有去详细的记录每一道题的得分,输出中的两个0全都是总得分。
改进建议:
在OutPut类的processAnswerSheet方法中,增加了scoreDetails字符串来跟踪每个问题的得分。
// 新增代码
StringBuilder scoreDetails = new StringBuilder();
// 修改后的循环部分
for (Paper.QuestionScore questionScore : answerSheet.getPaper().getQuestions()) {
int questionNumber = questionScore.getQuestionNumber();
Question question = findQuestionByNumber(questionNumber);
if (question == null) {
QuestionsresultS.add("the question " + questionNumber + " invalid~0");
scoreDetails.append("0 ");
continue;
}
String answer = answerSheet.getAnswers().get(questionNumber);
if (answer == null) {
QuestionsresultS.add("answer is null");
scoreDetails.append("0 ");
continue;
}
boolean isRight = answer.equals(question.getAnswer());
QuestionsresultS.add(question.getContent() + "~" + answer + "~" + isRight);
int score = isRight ? questionScore.getScore() : 0;
scoreDetails.append(score).append(" ");
totalScore += score;
}
// 修改后的输出
System.out.println(answerSheet.getStudent().getStudentNumber() + " " + answerSheet.getStudent().getName() + ": " + scoreDetails.toString().trim() + "~" + totalScore);
- 没有意识到答案没有输入这个错误的优先级最高
在答卷类没有输入答案的情况下输出报错的优先级应该是最高的,要比含错误格式输入、有效删除以及无效题目引用信息这些错误的优先级要高
改进意见:
答案检查逻辑:
在OutPut类的processAnswerSheet方法中,处理每个答题卡时,首先检查答案是否存在:
// 检查答案是否存在
String answer = answerSheet.getAnswers().getOrDefault(order, "");
if (answer.trim().isEmpty()) {
QuestionsresultS.add("answer is null");
scoreDetails.append("0 ");
continue;
}
这里使用getOrDefault(order, "")来获取答案,如果答案不存在则返回空字符串。然后通过trim().isEmpty()检查答案是否为空或只有空格。如果是,添加"answer is null"到结果列表,并继续处理下一个问题。
优先级控制:
由于这个检查逻辑在处理每个问题的最开始进行,因此它的优先级自然最高。即使后续有其他检查(例如检查题目是否被删除或题目是否存在),这些检查都在答案为空的情况下被跳过,因为代码在发现答案为空后立即continue到下一个循环迭代。
逻辑顺序:
代码逻辑顺序确保了在任何其他验证(如题目是否被删除、题目是否存在)之前,首先检查答案是否为空。这种顺序确保了"answer is null"的错误信息在答案缺失时优先被输出。 - 出现部分测试点非零返回
主要出现的问题就是无效试卷的引用。
改进意见:
if (answerSheet.isInvalidPaper()) {
System.out.println("The test paper number does not exist");
continue; // 直接跳过当前答卷的处理
}
if (answerSheet.isInvalidPaper() || answerSheet.isInvalidStudent()) {
continue;
}
当检测到试卷或学生无效时,立即使用continue语句跳过当前答卷的进一步处理。这避免了程序试图处理无效数据时可能出现的错误。 - 没有分清楚试卷号和顺序号
格式约束:
答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。
答案内容可以为空,即””。
答案内容中如果首尾有多余的空格,应去除后再进行判断。
样例:
#T:1 1-5 3-2 2-5 6-9 4-10 7-3
#S:1 20201103 #A:2-5 #A:6-4
1是试卷号
20201103是学号
2-5中的2是试卷中顺序号,5是试卷第2题的答案,即T中3-2的答案
6-4中的6是试卷中顺序号,4是试卷第6题的答案,即T中7-3的答案
注意:不要混淆顺序号与题号
int order = paper.getQuestions().indexOf(questionScore) + 1;
设置一个order来存顺序号。
String answer = answerSheet.getAnswers().getOrDefault(order, "");
if (answer.trim().isEmpty()) {
QuestionsresultS.add("answer is null");
scoreDetails.append("0 ");
continue;
}
检查答案是否存在,根据上面的样例可以看到答案类的去都是顺序号。
三、总结
在过去的几个月中,深入学习Java编程的过程中,我经历了从基础到复杂的多个项目挑战。这些项目不仅让我掌握了Java的语法和特性,还培养了我解决问题的能力和编程思维。
通过第一次作业,我巩固了面向对象编程的基础知识,理解了类的定义、构造方法、数据封装、常量定义和方法重写的重要性。这为我在后续项目中设计和实现复杂的类结构奠定了基础。
随着作业难度的增加,我开始接触到集合操作、字符串处理和输入输出等中级知识点。这些技能在第二次作业中得到了充分的应用。我学会了如何有效地解析和处理输入数据,特别是在应对格式复杂的输入时,如何利用正则表达式和字符串操作来提取有用的信息。
第三次作业则是对我编程能力的全面考验。面对多种输入类型、复杂的数据结构设计和多样的错误处理,我逐步提高了代码的健壮性和可维护性。我认识到,良好的代码结构和清晰的逻辑是解决复杂问题的关键。
在整个学习过程中,我也积累了许多经验和教训。比如,正确处理输入中的空格和格式问题,确保程序的输出与预期一致;在设计数据结构时,保持数据之间的关联性和一致性;在处理错误时,优先处理那些对程序影响最大的错误。
这些项目不仅提高了我的编程技术,也让我更加理解理论与实践相结合的重要性。每次解决一个难题或实现一个新功能,都会带给我极大的成就感和满足感。编程之路充满挑战,但每一次努力和探索都值得珍惜。未来,我将继续这段充满挑战的学习旅程,不断提升自己的技能和知识水平。