前言
在完成这三次题目集后,我也有了一些心得和体会。关于题目集1,它有5道题,分别是7-1:设计一个风扇Fan类;7-2:类和对象的使用;7-3:成绩计算-1-类、数组的基本运用;7-4:成绩计算-2-关联类;7-5:答题判题程序-1。在完成题目7-1时,我通过设计一个风扇类,了解了类的基本结构,对类的属性和方法也有了一定的了解。在完成题目7-2后,我对类和对象的使用更加熟练了。在完成题目7-3时,我对Java中数组的定义和使用有了一定的了解。在完成题目7-4后,我对类与类之间的关联也有了更加深入的理解。在完成最后的7-5后,我对类的属性,方法,类与类之间的关联和调用有了更加深入的了解。总体来说,题目集1的题目量较多,但是题目的难度不大,在花费一定的时间后,也可以顺利完成。
关于题目集2,它有4道题,分别是7-1:手机按价格排序、查找;7-2:sdut-oop-4-求圆的面积(类与对象);7-3:Java类与对象-汽车类;7-4:答题判题程序-2。其中题目7-1主要是考查Collections类的sort方法。而题目7-2,则是考查类与对象之间的关系,要用无参构造和有参构造两种构造方法来实现。题目7-3,则是完善代码,在完成题目后,有效地提高了我对类和对象地理解。最后地7-4,则是在题目集1的7-5的基础上,进一步增加要求,难度相对于原来的题目集1的7-5有了明显的提升。总的来说,题目集2的题目数量适中,难度一般。
关于题目集3,它只有三道题。分别是7-1:面向对象编程(封装性);7-2:jmu-java-日期类的基本使用;7-3:答题判题程序-3。题目7-1主要内容是完善代码,编写一个Student类,难度不大。题目7-2则是重点考查日期类的使用。题目7-3则是在题目集2的题目7-4基础上进一步增加要求,难度进一步提升。总的来说,题目集3的题目量较少,难度较大,要花费更多的时间完成。
设计与分析
题目集1:
(1)7-1
1.类 Fan
这是一个风扇类,用于表示风扇的属性和行为。
属性:
int SLOW = 1;:表示风扇的低速档位。
int MEDIUM = 2;:表示风扇的中速档位。
int FAST = 3;:表示风扇的高速档位。
int speed;:表示风扇的当前速度,值可以是 SLOW、MEDIUM 或 FAST。
boolean on;:表示风扇是否打开。true 为打开,false 为关闭。
double radius;:表示风扇的半径。
String color;:表示风扇的颜色。
构造方法:
无参构造方法:public Fan()创建一个默认的风扇对象
有参构造方法public Fan(int speed, boolean on, double radius, String color)可以根据传入的参数创建一个自定义的风扇对象,初始化 speed、on、radius 和 color。
方法:
toString() 方法,用于返回风扇对象的字符串表示。根据风扇的状态(开或关),返回不同的信息,显示速度、颜色和半径。
2.类 Main
功能:
创建默认风扇对象 fan1
获取用户输入
创建自定义风扇对象 fan2
输出风扇信息
总结:
类 Fan 定义了风扇的属性(速度、状态、半径和颜色)及其行为(构造方法和 toString 方法)。
类 Main 实现了程序的运行逻辑,创建风扇对象并通过用户输入来设置风扇的属性,最后展示风扇的状态。
相关类图如下
(2)7-2
- 类 Student
Student 类表示一个学生对象,包含学生的基本信息和相关操作。
属性:
private String name;:表示学生的姓名。
private String sex;:表示学生的性别。
private String studentID;:表示学生的学号。
private int age;:表示学生的年龄。
private String major;:表示学生的专业。
这些属性均为私有(private),只能通过类内的方法进行访问和修改。这种封装方式可以保证数据的安全性。
构造方法:
无参构造方法public Student() ,无参构造方法不初始化任何属性,可以在需要时使用默认值创建一个空的 Student 对象。
有参构造方法public Student(String name, String sex, String studentID, int age, String major) 有参构造方法允许通过参数直接初始化 name、sex、studentID、age 和 major 属性,方便创建带有特定信息的学生对象。
方法:
Getter 和 Setter 方法
每个属性都有对应的 getter 和 setter 方法,分别用于获取和设置属性值。
getName() 和 setName():获取和设置 name。
getSex() 和 setSex():获取和设置 sex。
getStudentID() 和 setStudentID():获取和设置 studentID。
getAge() 和 setAge():获取和设置 age。
getMajor() 和 setMajor():获取和设置 major。
这些方法实现了类的封装性,允许外部代码通过公共方法访问私有属性。
toString() 方法
toString() 方法返回 Student 对象的字符串表示,包含学生的所有属性值,并以中文字符描述信息。这对于输出学生信息非常有用。
printInfo() 方法
printInfo() 方法调用 toString() 并将信息打印到控制台,简化了信息输出的过程。
- 类 Main
Main 类为程序的主入口,主要用于从用户获取输入并展示学生的信息。
功能:
主方法 main()
获取用户输入
使用 Scanner 类从控制台获取用户输入。用户依次输入姓名、性别、年龄、专业和学号。
创建 Student 对象
Student student = new Student(name, sex, studentID, age, major);
使用有参构造方法创建 Student 对象 student,并将用户输入的值传递给对象的属性。
打印学生信息
student.printInfo();
调用 student 对象的 printInfo() 方法,打印学生的所有信息。
总结:
类 Student 封装了学生的基本信息及相关操作(如构造、信息输出等)。
类 Main 获取用户输入,通过 Student 类创建学生对象并展示信息。
相关类图如下
(3)7-5
-
Question 类
Question类表示单个题目,包括题号、题目内容和标准答案。
属性
private int num:题目编号。
private String question:题目内容。
private String standardAnswer:标准答案。
构造方法
public Question(int num, String question, String standardAnswer):初始化题目编号、内容和标准答案。
方法
public String getQuestion():获取题目内容。
public void setQuestion(String question):修改题目内容。
public boolean judge(String answer):判断用户答案是否与标准答案相同。 -
ExamPaper 类
ExamPaper类表示试卷,用于存储多个题目。该类通过ArrayList管理题目集合,允许动态调整题目数量。
属性
private Listquestions:存储题目对象的列表。
private int questionnumber:记录题目数量。
构造方法
public ExamPaper():初始化questions为空列表。
方法
public int getQuestionnumber():返回试卷中的题目数量。
public void setQuestionnumber(int questionnumber):设置题目数量,并填充questions列表。
public void addQuestion(int num, String question, String standardAnswer):在指定位置添加题目对象。
public String getQuestion(int num):获取指定题目的内容。
public boolean judge(int num, String answer):判断用户答案是否正确。 -
AnswerSheet 类
AnswerSheet类表示答题卡,用于记录用户的答案并进行批改。使用ArrayList管理答案和判断结果。
属性
private ExamPaper exam:当前答题卡关联的试卷对象。
private Listanswers:用户的答案列表。
private Listjudgments:判题结果列表。
构造方法
public AnswerSheet(ExamPaper exam):初始化答题卡,设置关联的试卷对象。
方法
public void addAnswer(String answer):向答案列表添加用户的答案。
public void judgeAll():遍历答案列表,对每道题进行判断,将结果添加到判题结果列表中。
public void outputAll():逐题输出题目内容和用户答案,然后输出判题结果。 -
Main 类
Main类是程序的入口,用于读取用户输入、创建题目和答案,并输出最终结果。
主方法 main()
初始化试卷:
ExamPaper exam = new ExamPaper();
exam.setQuestionnumber(Integer.parseInt(scanner.nextLine().trim()));
读取题目数量,并设置到ExamPaper实例中。
输入题目:
for (int i = 0; i < exam.getQuestionnumber(); i++) {
String line = scanner.nextLine().trim();
String[] parts = line.split("#N:|#Q:|#A:");
int num = Integer.parseInt(parts[1].trim());
String question = parts[2].trim();
String correctAnswer = parts[3].trim();
exam.addQuestion(num, question, correctAnswer);
}
按指定格式输入题目信息,使用正则表达式解析行内容,将题号、题目和标准答案存入ExamPaper中。
输入用户答案:
String answerInput = scanner.nextLine().trim();
String[] answerParts = answerInput.split("#A:");
for (String answerPart : answerParts) {
if (!answerPart.trim().isEmpty()) {
answerSheet.addAnswer(answerPart.trim());
}
}
用户答案通过拆分字符串处理,将各个答案记录到AnswerSheet中。
判题和输出:
answerSheet.judgeAll();
answerSheet.outputAll();
调用判题方法judgeAll批改试卷,并调用outputAll输出题目、用户答案和判题结果。
总结
Question:用于表示单个题目及其判断方法。
ExamPaper:用于管理题目列表,支持题目添加和判断。
AnswerSheet:记录用户的答案,并进行批改和结果输出。
Main:负责从控制台获取用户输入,输出结果,形成题库、答案和批改的完整流程。
相关类图如下
题目集2
(1)7-4
-
Question 类
Question类表示单个题目,包括题号、题目内容和标准答案。
属性:
private int num:题目编号。
private String question:题目内容。
private String standardAnswer:标准答案。
构造方法:
public Question(int num, String question, String standardAnswer):初始化题目编号、内容和标准答案。
方法:
public int getNum():获取题目编号。
public String getQuestion():获取题目内容。
public boolean judge(String answer):判断用户答案是否与标准答案相同。 -
ExamPaper 类
ExamPaper类表示试卷,用于存储多个题目。该类通过ArrayList管理题目列表,允许动态调整题目数量。
属性:
private int id:试卷编号。
private Listquestions:存储题目对象的列表。
private Map<Integer, Integer> questionScores:存储题目编号和对应分值的映射。
构造方法:
public ExamPaper(int id):初始化id和questions为题目列表,同时初始化questionScores为题目分值的映射。
方法:
public int getId():获取试卷编号。
public void addQuestion(Question question, int score):在试卷中添加题目和分值。
public Question getQuestion(int num):获取指定编号的题目对象。
public int calculateTotalScore():计算试卷所有题目的总分数。
public int getQuestionScore(int questionNum):获取指定题目编号的分值。
public ListgetQuestions():返回试卷的题目列表。 -
AnswerSheet 类
AnswerSheet类表示答题卡,用于记录用户的答案并进行批改。使用ArrayList管理答案和判断结果。
属性:
private ExamPaper exam:当前答题卡关联的试卷对象。
private Listanswers:用户的答案列表。
private Listjudgments:判题结果列表,记录每题是否答对。
private int totalScore:用户的总分数。
构造方法:
public AnswerSheet(ExamPaper exam):初始化答题卡,并关联试卷对象exam。
方法:
public void addAnswer(String answer):向答案列表中添加用户的答案。
public void judgeAll():遍历用户答案列表,对每道题进行判断,将判题结果记录到judgments列表中,并计算总分。
public void outputAll():输出每道题的内容、用户答案和判题结果,同时输出每题得分和总分。
public int getTotalScore():返回用户的总分数。 -
Main 类
Main类是程序的入口,用于读取用户输入,创建题目、试卷和答题卡对象,并输出最终结果。
主方法 main():
初始化试卷:
Map<Integer, ExamPaper> examPapers = new HashMap<>();
Map<Integer, Question> questionBank = new HashMap<>();
定义并初始化examPapers和questionBank分别用于存储试卷和题库信息。
输入题目:
if (line.startsWith("#N:")) {
String[] parts = line.split("#N:|#Q:|#A:");
int num = Integer.parseInt(parts[1].trim());
String questionText = parts[2].trim();
String standardAnswer = parts[3].trim();
questionBank.put(num, new Question(num, questionText, standardAnswer));
}
解析输入行内容,将题号、题目和标准答案存入Question对象,并将其添加到题库中questionBank。
输入试卷信息:
if (line.startsWith("#T:")) {
int paperId = Integer.parseInt(parts[0].substring(3));
ExamPaper examPaper = examPapers.getOrDefault(paperId, new ExamPaper(paperId));
}
解析试卷输入,创建ExamPaper对象,按题目编号和分值关联题目到试卷,若试卷总分不是100分,输出警告。
输入用户答案:
if (line.startsWith("#S:")) {
int paperId = Integer.parseInt(parts[0].substring(3));
ExamPaper examPaper = examPapers.get(paperId);
AnswerSheet answerSheet = new AnswerSheet(examPaper);
}
根据试卷编号查找试卷对象,创建AnswerSheet答题卡对象,并添加用户答案。
判题和输出:
answerSheet.judgeAll();
answerSheet.outputAll();
调用judgeAll()进行判分,调用outputAll()输出每题内容、用户答案及判题结果。
总结
Question:用于表示单个题目及其判断方法。
ExamPaper:用于管理题目列表,支持题目添加、分值计算和题目分值查询。
AnswerSheet:记录用户的答案,并进行批改和结果输出。
Main:负责从控制台获取用户输入,管理题库、试卷和答题卡的创建及处理,实现题库管理、答题和评分的完整流程。
相关类图如下
题目集3
(1)7-3
-
Question 类
Question 类表示单个题目对象,包含题号、题目内容和标准答案。主要用于题目的定义和有效性判断。
属性:
private int num:题目编号。
private String question:题目内容。
private String standardAnswer:标准答案。
private boolean valid:题目有效性标记。
构造方法:
public Question(int num, String question, String standardAnswer):初始化题目编号、内容和标准答案,默认设置题目有效。
方法:
public int getNum():获取题目编号。
public String getQuestion():获取题目内容。
public boolean isValid():判断题目是否有效。
public void invalidate():将题目标记为无效(已删除)。
public boolean judge(String answer):判断用户答案是否与标准答案一致。 -
ExamPaper 类
ExamPaper 类表示试卷,用于存储多个题目及其分值。通过 ArrayList 管理题目列表,通过 Map 记录题目编号与对应分值的映射关系。
属性:
private int id:试卷编号。
private Listquestions:存储题目对象的列表。
private Map<Integer, Integer> questionScores:存储题目编号和分值的映射。
构造方法:
public ExamPaper(int id):初始化试卷编号,初始化 questions 和 questionScores 为空的列表和映射。
方法:
public int getId():获取试卷编号。
public void addQuestion(Question question, int score):将题目及其分值添加到试卷中。
public Question getQuestion(int num):通过题目编号查找并返回 Question 对象。
public int getQuestionScore(int questionNum):获取指定题目的分值,若题目无效则返回 0。
public int calculateTotalScore():计算试卷的总分,仅累加有效题目的分值。
public ListgetQuestions():返回试卷的所有题目列表。 -
AnswerSheet 类
AnswerSheet 类表示答题卡,用于记录用户的答案并进行判分,使用 ArrayList 管理用户的答案列表和判题结果。
属性:
private ExamPaper exam:关联的试卷对象。
private String studentId:学生学号。
private String studentName:学生姓名。
private Listanswers:存储用户提交的答案。
private Listjudgments:记录每道题的判题结果。
private int totalScore:用户的总分。
构造方法:
public AnswerSheet(ExamPaper exam, String studentId, String studentName):初始化答题卡并关联到试卷,记录学生的学号和姓名。
方法:
public void addAnswer(String answer):添加用户答案到答题卡。
public void judgeAll():遍历用户答案,判分并记录每题结果,计算总分。
public void outputAll():输出每道题的题目、用户答案、判题结果、各题得分及总分。 -
Main 类
Main 类是程序的入口,负责读取用户输入,创建题目、试卷和答题卡对象,并输出判题结果。
主方法 main():
初始化试卷:
使用 Map<Integer, ExamPaper> 和 Map<Integer, Question> 分别存储试卷和题库。
输入题目:
通过 #N: 前缀读取题目输入,并创建 Question 对象,将其添加到 questionBank。
输入试卷信息:
通过 #T: 前缀读取试卷输入,创建 ExamPaper 对象,并按题目编号和分值关联题目到试卷中。
调用 calculateTotalScore 校验试卷总分是否为 100 分。
输入用户答案:
通过 #S: 前缀读取答卷输入,创建 AnswerSheet 对象,解析并添加用户答案。
题目删除:
通过 #D:N- 前缀读取删除命令,将 questionBank 中对应题目标记为无效,并同步更新所有 ExamPaper 中的题目状态,确保无效题目不计入总分。
判题和输出:
调用 answerSheet.judgeAll() 进行判分,调用 answerSheet.outputAll() 输出每题结果及总分。
总结
Question:表示单个题目,支持有效性判断和答案判定。
ExamPaper:表示试卷,管理题目列表及其分值,支持分数计算。
AnswerSheet:表示答题卡,记录用户答案并进行批改和结果输出。
Main:负责从控制台获取用户输入,管理题库、试卷和答题卡的创建与处理,实现题库管理、答题判分的完整流程。
程序分析类图如下
采坑心得
(1)在题目集1的题目7-5中,当输出样例输入多个题目,题号顺序与输入顺序不同时,我的代码会出错
如输入
2
N:2 #Q:1+1= #A:2
N:1 #Q:5+5= #A:10
A:10 #A:2
end
时,正确的样例是
5+5=~10
1+1=~2
true true
而我的代码的输出是
1+1=~10
5+5=~2
false false
经过检查后发现,我最开始的代码是根据题目输入顺序将题目内容存储到链表中,如以下代码所示
public void addQuestion(int num, String question, String standardAnswer) {
questions.add(new Question(num, question, standardAnswer));
}
这就导致了存答案时,会和题目的标准答案不同,比如1号位存的是第二题的答案,而2号位存的是问题1的答案,从而导致题目误判。在思考过后,修改代码如下
public void setQuestionnumber(int questionnumber) {
this.questionnumber = questionnumber;
this.questions = new ArrayList<>(questionnumber);
for (int i = 0; i < questionnumber; i++) {
questions.add(null); // 用 null 占位
}
}
public void addQuestion(int num, String question, String standardAnswer) {
questions.set(num-1,new Question(num, question, standardAnswer));
}
先将链表所有位置的值都赋null,再用set方法直接修改对应的位置的值,从而解决了这个问题。
(2)在题目集3的题目7-3中,代码输入
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
时,输出的结果为
alert: full score of test paper1 is not 100 points
1+1=5false
the question 2 invalid~0
20201103 Tom: 0 0~8
而正确的结果为
alert: full score of test paper1 is not 100 points
1+1=5false
the question 2 invalid~0
20201103 Tom: 0 0~0
经过分析后得知,代码在删除题目后,题目答对仍然计分了,于是重新修改判定条件如下
if (question.isValid()) { // 有效题目
if (i < answers.size()) { // 用户提供了答案
String answer = answers.get(i).trim();
boolean isCorrect = question.judge(answer);
judgments.add(isCorrect);
if (isCorrect) {
totalScore += exam.getQuestionScore(question.getNum());
}
}
很好地解决了这个问题。
改进建议
(1)优化 AnswerSheet 类中的判题逻辑
目前的判题逻辑会依赖输入的答案顺序,而不是题目编号。可以使用题目编号作为主键来存储答案和判题结果,以提高正确性和灵活性,防止答案顺序出错。例如,可以使用 Map<Integer, String> answers 和 Map<Integer, Boolean> judgments。
private Map<Integer, String> answers; // 题目编号 -> 答案
private Map<Integer, Boolean> judgments; // 题目编号 -> 判题结果
public void addAnswer(int questionNum, String answer) {
answers.put(questionNum, answer);
}
这样,判题时可以直接通过题目编号来访问答案,从而避免因为输入顺序或题目被删除而导致的判分错误。
(2). 封装重复逻辑,简化 Main 类
Main 类目前包含大量的输入解析和处理逻辑,显得有些冗长。可以将不同输入类型的解析(如题目、试卷、学生和答卷的输入)封装成独立的方法,让 main() 方法更加简洁。示例:
private static void handleQuestionInput(String line, Map<Integer, Question> questionBank) { ... }
private static void handleExamPaperInput(String line, Map<Integer, ExamPaper> examPapers, Map<Integer, Question> questionBank) { ... }
private static void handleStudentInput(String line, Map<String, String> studentInfo) { ... }
private static void handleAnswerSheetInput(String line, Map<Integer, ExamPaper> examPapers, Map<String, String> studentInfo, List
将这些方法独立出来,可以增强代码的可读性和维护性,也便于将来扩展或修改。
(3)提升 Question 的判定能力
目前 Question 类的 judge 方法仅支持完全匹配的答案。可以考虑加入对大小写不敏感或不同格式的支持,以提高程序的适用性。例如:
public boolean judge(String answer) {
return answer.trim().equalsIgnoreCase(standardAnswer.trim());
}
这样可以确保用户的答案大小写、前后空格不影响评分。
总结
在完成本次题目设计过程中,我学习并应用了面向对象编程的基本原则和方法,包括类的设计、数据封装、类之间的交互,以及如何通过代码实现复杂系统的功能。在设计答题管理系统的过程中,我逐步加深了对代码结构、模块化设计、输入输出处理以及异常处理的理解。整个项目的实现从题库管理、试卷生成到答题判分,涉及多个类之间的协作与数据流动,锻炼了我在代码设计和逻辑推理上的能力。同时通过设计 Question.ExamPaper 和 AnswerSheet 类,我加深了对面向对象设计原则的理解,学会了如何封装、继承和多态等基本概念的应用。在完成题目的过程中,我使用了 Java 中的 ArrayList 和 HashMap 来管理动态的数据结构,熟悉了如何有效地存储和检索数据。在处理用户输入时,我学会了如何使用字符串处理方法,如 split() 和 trim(),以及如何从输入中提取有用信息并进行数据校验。在完成题目后,我也意识到了自己的代码不足,比如虽然 ArrayList 和 HashMap 可以应对当前的题目需求,但如果系统规模扩大,需要使用更加复杂的数据结构和算法来优化性能。在以后的时间里,我会进一步完善我的代码,争取精益求精。