Java大作业总结
- Java大作业总结
- 一.前言
- 第一次作业
- 1.设计与分析
- 第一小题
- 第二小题
- 第三小题
- 第四小题
- 第五小题
- 2.踩坑心得
- 3.改进建议
- 1.设计与分析
- 第二次作业
- 1.设计与分析
- 第一小题
- 第二小题
- 第三小题
- 第四小题
- 2.踩坑心得
- 3.改进建议
- 1.设计与分析
- 第三次作业
- 1.设计与分析
- 第一小题
- 第二小题
- 第三小题
- 2.踩坑心得
- 3.改进建议
- 1.设计与分析
- 总结
一.前言
这三次大作业前面的题目都比较简单,题量适中,题目集中的最后一题是最难的,在第三次大作业相较于前两次的大作业的最后一题也是最难的。这三次大作业能循序渐进,难度增大幅度快。让我学会更多Java的知识。本次总结粗略分析每个题目集的前面几个小题,重点分析每个题目集的最后一题。
第一次作业
1.设计与分析
-
第一小题
这题根据题目所要求,在风扇类创建相关的成员变量,然后再重写一下toString
方法,在main方法中直接打印风扇类的对象,即可打印出toString
里面所要求的样式。
-
第二小题
第二小题和第一题差不多,都是按照要求创建相应的成员变量,主要的是这道题要求toString
方法和printInfo
方法,这2个方法要求输出的内容不同。
-
第三小题
第三小题主要是输入的信息的划分比较麻烦,浮点数运算比较难,在题目中给出浮点数运算的链接中,给出了4中方法,其中我认为String.format("%.2f", f)
和DecimalFormat
比较好用,在我使用过程中BigDecimal
向上或向下取整没用。
-
第四小题
第四小题和第三小题类似,不做赘述,主要注意的是题目中之说了运算2个平均分,但是在样例中得算2个总分和2个平均分。
-
第五小题
这题难的是对于输入信息的划分,开始时我用的不是正则表达式,划分起来很麻烦,有很多情况要考虑,是否有空格,输入是
合法,这些都要考虑,后来就换用了正则表达式。首先读取题目的数量,根据题目数量循环调用正则表达式方法读取题目信息。匹配到内容后,用Map存储
Map<Integer, Question> questions = new LinkedHashMap<>();
其中键为题号,值为一个Question类型的对象,Question里面有2个成员变量
- content - 题目的内容
- answer - 题目的答案
分割题目,答案,存储题目信息的逻辑如下代码所示
String numberStr = extractPart(line, "#N:");String content = extractPartWithSpaces(line, "#Q:");String answer = extractPart(line, "#A:");int number = Integer.parseInt(numberStr.trim());questions.put(number, new Question(content, answer));
现在已经读取到了题目数量,题目,和标准答案,接着就要读取考生输入的答案了,由于所有的题目答案都是在一行输入
#A:2 #A:4
那就还得分割一下答案信息,同时用一个List存储答案
List<String> answers = new ArrayList<>(); String[] userAnswers = line.split("#A:");for (int j = 1; j < userAnswers.length; j++) { // 跳过第一个空字符串answers.add(userAnswers[j].trim());}
存储好题目,标准答案和考生输入的答案后,就得输出题目考生输入的答案和true
或false
了。
先是遍历questions
这个Map,根据题号获取题目信息并输出,接着补上‘~’号再遍历answers
这个List,输出对应的考生的答案,最后判断考生输入答案是否是标准答案而输出对应结果。
总的来说代码逻辑就是
- 首先读取题目数量。
- 接着逐条读取题目信息,并使用
LinkedHashMap
存储题目信息,确保题目按输入顺序被处理。 - 读取用户答题信息直到遇到“end”标记停止。
- 输出每道题的题目内容及用户答案。
- 对比用户答案与标准答案,输出判题结果。
输入处理
- 使用
extractPart
和extractPartWithSpaces
方法解析输入行中的题号、题目内容和标准答案。 - 题目信息存储在一个
LinkedHashMap
中,键为题号,值为题目对象。 - 用户答案存储在一个
ArrayList
中。
输出处理
- 输出部分首先遍历
LinkedHashMap
,打印出每道题的题目内容和用户的答案。 - 然后再次遍历
LinkedHashMap
,ArrayList
,和通过比较用户的答案和题目对象的标准答案来构建判题结果字符串。 - 最后输出判题结果。
类图
2.踩坑心得
- 刚开始时用的是字符串分割,一个一个匹配,但是后面要考虑的情况实在是太多了,题目中可能有空格,题目不是数学公式等问题,因此后面换了正则表达式,可以直接匹配题目,不用考虑各种可能出现的情况。
String pattern = prefix + "\\s*(.*?)(?=\\s*#A:|$)";
String pattern = prefix + "\\s*(.*?)(?:\\s+|$)";
-
刚开始时不是用Map存储题目的题号和题目内容,而是用List只存储题目内容,但是遇到
2 #N:2 #Q:1+1= #A:2 #N:1 #Q:5+5= #A:10 #A:10 #A:2 end
这种示例时,程序会先输出第二题而不是第一题,导致出错。因此后面改用Map存储题号和题目内容。
3.改进建议
- 这次作业基本上没有用到类的思想,只定义了一个问题类和一个Main类。
第二次作业
1.设计与分析
-
第一小题
这道题主要是运用接口,实现类得重写接口的方法。比较手机的价格,同时按升序排序的话又得重写Collection
类的sort
方法.
-
第二小题
这题主要是构造方法的运用,可以用无参构造方法,也可以用有参构造方法来创建一个对象。
-
第三小题
这题只要补充代码就行,而补充的代码就是对类里面成员变量的操作。
-
第四小题
看题目就知道要划分题目类,试卷类,答卷类,我又自己定义了个扫描试卷类和评分类,减少Main类的代码
题目类有3个参数
- number - int类型,题号
- content - String类型,题目内容
- answer - String类型,题目标准答案
试卷类有3个参数
- id - int类型,试卷号
- questionScores -
List<Map.Entry<Integer, Integer>>
类型,里面存储了一个Map,Map键为题号,值为分数 - totalScore - int类型,总分
答卷类有2个参数
- testPaperId - int类型,试卷号
- answers -
List<String>
类型,存储考生答案
扫描类有3个参数
- questions -
Map<Integer, Question>
类型,存储题目编号和Question对象 - testPapers -
Map<Integer, TestPaper>
类型,存储试卷号和TestPaper对象 - answerSheets -
List<AnswerSheet>
类型,存储AnswerSheet对象
-
由于要输入试卷号,题目和考生答案等太多内容,用正则表达式解析不过来,因此我选择字符串分割,自己解析对应数据。在扫描数据时,我选择在循环一行一行扫描解析,遇到输入的是end就结束解析,每一行都会判断开头是否是#N,#T,#S,然后在进入对应的解析代码中,进入对应解析代码中,还得按空格用
split
进行分割数据,接着再进行对应操作,然后用有参构造方法创建题目/试卷/答卷的对象,最后用集合存储对象,扫描工作即可完成。 -
在评分类中,定义一个总分数组,先把每个相同试卷号的小题分数相加,若不足100分就提示“alert: full score of test paperX is not 100 points”,同时如果答案信息中试卷的编号找不到也要输出对应提示“The test paper number does not exist”。
至此才正式到了判断环节,先变量试卷类中的questionScores
成员变量取出每一个Map,然后再遍历Map,获取题号,根据题号从扫描类的questions获取题目类对象,同时判断题目对象中的标准答案和考生输入的答案是否相同,相同就把总分数组的分数相加,计算出总分,若是在answerSheets里面找不到题号对应的答案,输出“answer is null”。最后输出题目
for (Map.Entry<Integer, Integer> entry : testPaper.questionScores) {int questionNumber = entry.getKey();int questionScore = entry.getValue();Question question = scanPaper.questions.get(questionNumber);String studentAnswer = answerIterator.hasNext() ? answerIterator.next() : null;boolean correct = studentAnswer != null && studentAnswer.equals(question.answer);totalScore += correct ? questionScore : 0;scores[scores.length - testPaper.questionScores.size() + testPaper.questionScores.indexOf(entry)] = correct ? questionScore : 0;if (studentAnswer == null) {resultOutput.append("answer is null\n");} else {resultOutput.append(question.content).append("=").append("~").append(studentAnswer).append("~").append(correct).append("\n");}}
类图
2.踩坑心得
- 刚开始解析题目时,我用的是
String[] parts = line.split(" ");
因为样例中没有题目中有空格的情况,但是实际测试中有2个样例非零返回,后面查找原因是题目有空格,导致题目被划分开了,从而导致Integer.parseInt
这个方法解析的不是数字,可能是运算符号等等。
3.改进建议
- 这次作业里面的参数命名不好,有试卷号,有题号,试卷号和题号在其他类中也有,导致容易混淆,代码看的费劲。
- 引入自定义异常类,并记录详细的错误日志。例如,可以创建
InputFormatException
等异常类,当检测到输入格式错误时抛出,并记录具体的错误信息。 - 考虑使用更高效的数据结构来提高程序的性能。例如,使用
TreeMap
来替代HashMap
进行有序存储,或者使用ConcurrentHashMap
来支持并发访问。
第三次作业
1.设计与分析
-
第一小题
这道题主要是运用getter
方法和setter
方法
-
第二小题
这题主要是熟悉日期类的使用,如何格式化日期和解析日期,格式化日期用到了DateTimeFormatter
类和LocalDate
类,LocalDate
类能很好的判断该日期是是当年的第几周,第几天,该年份是否是闰年等。
-
第三小题
这道题我是在第二次大作业基础上改的,增加了学生类和删除类,同时还修改了参数的名字。
题目类有3个参数
- number - int类型,题号
- content - String类型,题目内容
- answer - String类型,题目标准答案
试卷类有3个参数
- id - int类型,试卷号
- questionNumAndScores-
List<Map.Entry<Integer, Integer>>
类型,里面存储了一个Map,Map键为题号,值为分数 - totalScore - int类型,总分
答卷类有3个参数
-
studentID - String类型,学生编号
-
testPaperId - int类型,试卷号
-
questionNumAndAnswer-
List<String>
类型,存储考生答案
扫描类有3个参数
- questions -
Map<Integer, Question>
类型,存储题目编号和Question对象 - testPapers -
Map<Integer, TestPaper>
类型,存储试卷号和TestPaper对象 - answerSheets -
List<AnswerSheet>
类型,存储AnswerSheet对象 - cun - int类型,计题目数量
- ErrorCun - int类型,出错的题目数量
- students -
List<Student>
类型,存储学生对象 - deleteQuestionsList -
List<deleteQuestions>
类型,存储删除类的
删除类有1个参数
- QuestionNum - int类型,所删除的题目的编号
在扫描类输入题目信息时,本来应该输入#N:1 #Q:1+1= #A:2
却输入成#N:1 +1= #A:2
,这会导致代码解析出错,从而导致整个程序退出运行,那么直接加入try{}catch{}
语句,同时在catch代码块里面输出题目让我们输出的信息,这既能解决解析出错导致整个程序崩溃,还能符合题目要求。同时题目还增加了删除题目的功能,因此要在questions
集合删除对应的题目,但是这也会导致一个问题:
Question question = scanPaper.questions.get(questionNumber);
可能要获取删除的题目,那么值就为空,因此这里要加一个判断若为空,就提示题目要我们输出对应的信息。在判断为空的地方还得判断是否是题目不存在还是答案不存在还是问题无效这三种情况。
类图
2.踩坑心得
-
这次作业没有看见还有输入格式错误的样例,最后找到原因,本来向另外判断的,但是看见样例如果输入格式错误都是最优先打印的,因此直接用try catch语句把错误提示打印出来。
-
由于删掉了一个题目,但是在评分类还获取了这个题目,导致整个程序崩溃。
3.改进建议
ratingSystem
类中直接访问scanPaper
类的静态成员变量是不好的设计模式。可以通过构造函数传递所需的对象实例来解决这个问题。- 考虑将输入解析逻辑从
scanning()
方法中提取出来,使得scanPaper
类的职责更加单一。 - 一些方法名可以更清晰地描述其功能,比如
gradeSheet
可以改为evaluateAnswerSheet
,使其意图更为明确。
总结
这三次作业难度不断提高,特别是答题判断程序。同时也学会了很多,比如正则表达式,类的单一原则,时间类的使用,集合的使用和集合的遍历,集合的嵌套等知识......,对于Java提供的集合的遍历特别是Map的方法,还需要还有集合的迭代器也是。