23201630徐弘-第一次blog作业
前言:
本次三周的最后一道题,都是答题判题程序,1,2,3道题难度逐级增加。
第一题 :
第一题的知识点在于类之间的三个类之间的关联设计,试卷类关联了题目类,答卷类又关联了试卷类,层层关联,然后便是各个类之间的设计,其中包括的对象和方法,题目类中包含判断题目对错的方法,试卷类调用这个方法,并在答卷类中储存。而在难度方面,主要在于对于每行输入的信息的分析,究竟是答卷,还是题目,不过还好本题是直接输入题目数量,然后按照题目数量输入。这个程序主要涉及考试试卷的创建、答题、批改等功能。它的知识点和难度可以分为以下几个方面:
- 面向对象编程(OOP):
o 类的定义和使用:Test、TestPaper、AnswerPaper类的定义及其方法的使用。
o 封装:私有属性的封装,通过公有方法来访问私有属性。
o 构造函数:Test、TestPaper、AnswerPaper类的构造函数的使用。
o 方法调用:调用TestPaper中的markTest方法、调用AnswerPaper中的printQ_A方法等。 - 数组和数组操作:
o 使用数组存储试题和答案。
o 遍历数组进行操作,如计算得分、输出试题和答案等。 - 字符串处理:
o 使用String类的split方法根据特定字符分割字符串。
o 使用String类的startsWith和substring方法处理特定格式的输入数据。 - 输入输出操作:
o 使用Scanner类实现从标准输入读取数据。
o 使用System.out.println输出结果。 - 难度方面,这个程序整体上属于初级到中级水平。其中,最大的挑战可能是对字符串的处理和数据分割,以及对类与对象的理解和运用。逻辑比较清晰,没有复杂的算法或数据结构,因此对于熟悉面向对象编程和基本输入输出操作的人来说,应该不算太难。
第二题:
在第一题的基础上增加了难度,增加了多个试卷, 而且答案不与卷子的题目完全相对应,并且增加了判分的功能,整体难度有所上升
以下是代码中涉及的主要知识点和难度:
Java基础知识:代码中运用了Java的基本语法,如类的定义、构造函数、成员变量、方法的调用等。
正则表达式:使用了正则表达式来解析输入的字符串,提取试题编号、题目和标准答案。
集合框架:利用了Java集合框架中的LinkedHashMap、ArrayList等来存储试题、试卷和答题情况。
字符串处理:通过split()方法和substring()方法对输入的字符串进行分割和提取。
流程控制:使用了while循环和条件判断语句来控制程序的流程。
异常处理:代码中没有显式的异常处理,对于输入的合法性和格式没有做过多的检查和处理。
算法逻辑:对学生答题进行了评分,判断答案是否正确,并计算总分。但代码实现上存在一些逻辑错误,例如计算分数时的逻辑问题以及部分输出的处理。
程序结构设计:整体设计上,分为试题录入、试卷生成和学生答题三个部分,但代码结构可以进一步优化,提高可读性和可维护性。
总体来说,这段代码涉及的知识点主要集中在Java基础、集合框架和字符串处理等方面,难度属于中等水平,但需要注意一些逻辑错误和代码结构上的改进空间。
第三题:
在第二题的基础上又增加了难度,使之在判题方面变得更加灵活,还新增了删除题目信息,能使题目失效
主要涉及以下几个知识点和概念:
1、代码使用了类和对象(比如 Question, TestPaper, AnswerSheet 等),这是面向对象编程的基本。通过对相关数据和功能的封装,使得代码结构更为清晰。
2、集合框架的使用:代码中广泛使用了Java的集合框架,比如 List 和 ArrayList。这包括遍历集合、访问元素、基于条件的检索等操作。
3、条件判断:代码多次使用了 if 条件判断语句来检查各种条件,如题目信息是否符合格式、试卷总分是否正确以及答案是否正确等。
4、循环结构:代码中使用了多层嵌套的 for 循环来遍历试卷、题目和答案。这种数据结构的遍历是编程中常见的需求。
5、方法定义和调用:代码通过定义和调用多个方法(如 checkQuestionFormat, checkTestPaperTotalScore, checkAnswerSheet),将功能细分,提高了代码的模块化和可重用性。
6、字符串操作:使用了字符串的基本操作,如 isEmpty() 检查字符串是否为空,以及字符串的比较操作。
7、打印输出:使用 System.out.println 来输出信息,这是基本的调试技术,帮助开发者了解程序的运行状态。
从难度角度分析,这段代码的难度属于中等水平:
总的来说,其中涵盖了多个基本的编程构件和面向对象的设计,使我加深对了Java编程和面向对象设计原则的理解。
设计与分析:
第一题:
`import java.util.*;
class Test {
private int num;
private String test;
private String standardAnswer;
public Test(int num, String test, String standardAnswer) {
this.num = num;
this.test = test;
this.standardAnswer = standardAnswer;
}
public String getStandardAnswer() {
return standardAnswer;
}
public String getTest() {
return test;
}
public boolean isCorrect(String answer) {
return standardAnswer.equals(answer);
}
}
class TestPaper {
Test[] tests;
public TestPaper(int testNum) {
tests = new Test[testNum];
}
public void inputTest(int num, Test test) {
tests[num - 1] = test;
}
public boolean markTest(int num, String answer) {
return tests[num - 1].isCorrect(answer);
}
}
class AnswerPaper {
private TestPaper paper;
private String[] answers;
private boolean[] marks;
public AnswerPaper(TestPaper paper, String[] answers) {
this.paper = paper;
this.answers = answers;
marks = new boolean[answers.length];
calculateMarks();
}
private void calculateMarks() {
for (int i = 0; i < answers.length; i++) {
marks[i] = paper.markTest(i + 1, answers[i]);
}
}
public void printQ_A() {
for (int i = 0; i < paper.tests.length; i++) {
System.out.println(paper.tests[i].getTest() + "~" + answers[i]);
}
for (int i = 0; i < paper.tests.length; i++) {
if(i!=0){
System.out.print(" ");
}
System.out.print(marks[i]);
}
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
TestPaper testPaper = new TestPaper(n);
String[] answers = new String[n];
List
while (sc.hasNextLine()) {
String line = sc.nextLine().trim();
if (line.equals("end")) {
break;
}
inputLines.add(line);
}
int i = 0;
for (String line : inputLines) {
if (line.startsWith("#N:")) {
String[] parts = line.split("#");
int num = Integer.parseInt(parts[1].substring(2).trim());
String test = parts[2].substring(2).trim();
String standardAnswer = parts[3].substring(2).trim();
Test newTest = new Test(num, test, standardAnswer);
testPaper.inputTest(num, newTest);
} else if (line.startsWith("#A:")) {
String[] parts2 = line.split("#");
for (String part : parts2) {
if (!part.isEmpty()) {
answers[i++] = part.substring(2).trim();
}
}
}
}
AnswerPaper answerPaper = new AnswerPaper(testPaper, answers);
answerPaper.printQ_A();
}
}`

第一题,在编写的过程中,通过在试卷类中创建题目类,实现了题目与试卷的关联,然后在答案类里又创建了试卷类,方便答案的对错判断,最后在main中将信息输入,然后分析每行信息,存入相应的对象中,最后按要求输出答案
第二题:
`import java.util.;
import java.util.regex.;
class ExamQuestion {
private String id;
private String question;
private String standardAnswer;
public ExamQuestion(String id, String question, String standardAnswer) {
this.id = id;
this.question = question;
this.standardAnswer = standardAnswer;
}
public String getId() {
return new String(id);
}
public void setId(String id) {
this.id = id;
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
public String getStandardAnswer() {
return standardAnswer;
}
public void setStandardAnswer(String standardAnswer) {
this.standardAnswer = standardAnswer;
}
}
class TestPaper {
String num;
LinkedHashMap<String, Integer> questions;
public TestPaper(String num) {
this.num = num;
this.questions = new LinkedHashMap<>();
}
public void addQuestion(String questionNumber, int score) {
questions.put(questionNumber, score);
}
}
class Answer {
String num;
List
public Answer(String num) {
this.num = num;
this.answers = new ArrayList<>();
}
public void addAnswer(String answer) {
answers.add(answer);
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
boolean flag = true;
String regex = "#N:(\d+) #Q:(.) #A:(.)";
LinkedHashMap<String, TestPaper> testPaperMap = new LinkedHashMap<String, TestPaper>();
LinkedHashMap<String, ExamQuestion> questionMap = new LinkedHashMap<String, ExamQuestion>();
List
while (flag) {
String str = sc.nextLine();
String num;
if (str.equals("end")) {
flag = false;
break;
} else if (str.startsWith("#N:") || str.startsWith("#Q:")) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
String x1 = matcher.group(1);
String x2 = matcher.group(2);
String x3 = matcher.group(3);
questionMap.put(x1, new ExamQuestion(x1, x2, x3));
}
} else if (str.startsWith("#T:")) {
String[] s = str.split(" ");
num = s[0].substring(3);
TestPaper test = new TestPaper(num);
for (int i = 1; i < s.length; i++) {
String[] pair = s[i].split("-");
test.addQuestion(pair[0], Integer.parseInt(pair[1]));
}
testPaperMap.put(num, test);
} else if (str.startsWith("#S:")) {
String[] parts = str.split(" ");
num = parts[0].substring(3);
Answer answer = new Answer(num);
for (int i = 1; i < parts.length; i++) {
answer.addAnswer(parts[i].substring(3));
}
answers.add(answer);
}
}
int j=0;
String x;
StringBuilder sb=new StringBuilder();
for (Answer answer : answers) {
TestPaper test = testPaperMap.get(answer.num);
int total = 0;
int m=0;
for (int i = 0; i < answer.answers.size(); i++) {
String questionNumber = test.questions.keySet().toArray()[i].toString();
String a = answer.answers.get(i);
String correctAnswer = questionMap.get(questionNumber).getStandardAnswer();
boolean isCorrect = a.equals(correctAnswer);
m=m+ test.questions.get(questionNumber);
int score =isCorrect ? test.questions.get(questionNumber) : 0;
x=Integer.toString(score);
sb=new StringBuilder(x);
sb=sb.append(" "+sb);
if(isCorrect){
total += score;
}
}
if (m != 100&&j==0) {
System.out.println("alert: full score of test paper" + test.num + " is not 100 points");
j=1;
}
for (int i = 0; i < answer.answers.size(); i++) {
String questionNumber = test.questions.keySet().toArray()[i].toString();
String a = answer.answers.get(i);
String correctAnswer = questionMap.get(questionNumber).getStandardAnswer();
boolean isCorrect = a.equals(correctAnswer);
System.out.println(questionMap.get(questionNumber).getQuestion() + "~" + a + "~" + isCorrect);
}
StringBuilder scores = new StringBuilder();
for (String questionNumber : test.questions.keySet()) {
scores.append(test.questions.get(questionNumber)).append(" ");
}
System.out.print(sb + "~" + total);
}
}
}`

第二道题主要删除了n行答案输入的限制,转而判断每行的信息属于哪一类,再将信息存入对象的数组中,因为输入的数量不确定,所以使用了ArrayList()类集合来储存,再用增强循环遍历来判断得分
第三题:
`import java.util.*;
class Question {
int number;
String content;
String answer;
public Question(int number, String content, String answer) {
this.number = number;
this.content = content;
this.answer = answer;
}
}
class TestPaper {
int number;
List
List
public TestPaper(int number, List<Integer> questionNumbers, List<Integer> questionScores) {
this.number = number;
this.questionNumbers = questionNumbers;
this.questionScores = questionScores;
}
}
class Student {
int id;
String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
class AnswerSheet {
int testPaperNumber;
Student student;
List
public AnswerSheet(int testPaperNumber, Student student, List<String> answers) {
this.testPaperNumber = testPaperNumber;
this.student = student;
this.answers = answers;
}
}
public class Main {
public static void main(String[] args) {
// 读取输入
Scanner scanner = new Scanner(System.in);
List
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (line.equals("end")) {
break;
}
inputLines.add(line);
}
scanner.close();
// 解析输入,创建题目信息、试卷信息、学生信息和答卷信息
List
List
List
List
for (String line : inputLines) {
if (line.startsWith("#N:")) {
// 解析题目信息
String[] parts = line.split("\\s+");
int number = Integer.parseInt(parts[0].substring(3));
String content = parts[1].substring(3);
String answer = parts[2].substring(3);
Question question = new Question(number, content, answer);
questions.add(question);
} else if (line.startsWith("#T:")) {
// 解析试卷信息
String[] parts = line.split("\\s+");
int number = Integer.parseInt(parts[0].substring(3));
List<Integer> questionNumbers = new ArrayList<>();
List<Integer> questionScores = new ArrayList<>();
for (int i = 1; i < parts.length; i++) {
String[] qInfo = parts[i].split("-");
questionNumbers.add(Integer.parseInt(qInfo[0]));
questionScores.add(Integer.parseInt(qInfo[1]));
}
TestPaper testPaper = new TestPaper(number, questionNumbers, questionScores);
testPapers.add(testPaper);
} else if (line.startsWith("#X:")) {
// 解析学生信息
String[] parts = line.split("\\s+");
for (int i = 1; i < parts.length; i++) {
String[] sInfo = parts[i].split("-");
int id = Integer.parseInt(sInfo[0]);
String name = sInfo[1];
Student student = new Student(id, name);
students.add(student);
}
} else if (line.startsWith("#S:")) {
// 解析答卷信息
String[] parts = line.split("\\s+");
int testPaperNumber = Integer.parseInt(parts[1]);
int studentId = Integer.parseInt(parts[2]);
Student student = null;
for (Student s : students) {
if (s.id == studentId) {
student = s;
break;
}
}
List<String> answers = new ArrayList<>();
for (int i = 3; i < parts.length; i++) {
answers.add(parts[i].substring(3));
}
AnswerSheet answerSheet = new AnswerSheet(testPaperNumber, student, answers);
answerSheets.add(answerSheet);
} else if (line.startsWith("#D:")) {
// 处理题目删除信息
String[] parts = line.split("\\s+");
for (int i = 1; i < parts.length; i++) {
String[] dInfo = parts[i].split("-");
int number = Integer.parseInt(dInfo[1]);
for (Iterator<Question> iterator = questions.iterator(); iterator.hasNext();) {
Question question = iterator.next();
if (question.number == number) {
iterator.remove();
break;
}
}
}
}
}
// 检查题目信息是否符合要求
for (Question question : questions) {
if (question.content.isEmpty() || question.answer.isEmpty()) {
System.out.println("wrong format:" + "#N:" + question.number + " " + "#Q:" + question.content + " " + "#A:" + question.answer);
}
}
// 判断试卷总分是否为100分
for (TestPaper testPaper : testPapers) {
int totalScore = 0;
for (int score : testPaper.questionScores) {
totalScore += score;
}
if (totalScore != 100) {
System.out.println("alert: full score of test paper" + testPaper.number + " is not 100 points");
}
}
// 判断答卷中的题目是否存在,分数是否正确
for (AnswerSheet answerSheet : answerSheets) {
TestPaper testPaper = null;
for (TestPaper tp : testPapers) {
if (tp.number == answerSheet.testPaperNumber) {
testPaper = tp;
break;
}
}
if (testPaper == null) {
System.out.println("The test paper number does not exist");
continue;
}
int totalScore = 0;
for (int i = 0; i < testPaper.questionNumbers.size(); i++) {
int qNumber = testPaper.questionNumbers.get(i);
int score = testPaper.questionScores.get(i);
boolean found = false;
for (Question question : questions) {
if (question.number == qNumber) {
found = true;
if (i < answerSheet.answers.size()) {
String answer = answerSheet.answers.get(i);
if (answer.equals(question.answer)) {
totalScore += score;
}
System.out.println(question.content + "~" + score + "~" + answer.equals(question.answer));
}
break;
}
}
if (!found) {
System.out.println("non-existent question~0");
}
}
if (answerSheet.answers.size() < testPaper.questionNumbers.size()) {
System.out.println("answer is null");
}
System.out.println(answerSheet.student.id + " " + answerSheet.student.name + ": " + totalScore + " " + totalScore);
}
}
}
`

第三题其实不怎么会写,增加了删除题目信息类,使被删除的题目信息无效化,然后记录下并使相应的分值变为0.
踩坑心得:
1、在使用split切割字符串的时候,第一个字符串是空,导致赋值储存的时候报错,无法存进
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin 2, end 0, length 0 at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4601) at java.base/java.lang.String.substring(String.java:2704) at java.base/java.lang.String.substring(String.java:2677) at Main.main(Main.java:94)
经过调试后发现了第一个字符串为空,然后添加了个判断字符串是否为空“isEmpty()”,再存入。

2、在使用完split后忽略了其将“#”去除了,在用substring提取字符串的时候(2)填写成了(3),使得最后的答案无法比配成功。
Exception in thread "main" java.lang.NumberFormatException: For input string: "" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:678) at java.base/java.lang.Integer.parseInt(Integer.java:786) at Main.main(Main.java:94)
将3改为2后,代码得以正常运行。
3.无法用试卷号直接找到相应的试卷
解决办法是,运用HashMap键值对,将题号与试卷类强关联,使我们能直接通过试卷序号找到试卷类。
改进意见:
异常处理:在代码中加入异常处理,以处理可能发生的输入错误或异常。这将提高代码的健壮性,并避免意外崩溃。
变量命名:调整变量名称,使其更具描述性。例如,将num改为questionNumber、testPaperNumber或answerNumber等,以便更好地理解其作用。
减少重复代码:例如,遍历answer.answers两次,可以合并成一次遍历。此外,对于获取问题编号和答案的代码,可以重构为一个方法,以减少重复。
代码重构:代码中有些部分可以提取为独立的函数以提高可读性。例如,解析输入字符串的逻辑可以提取为一个单独的函数。
数据结构的选择:目前代码使用了LinkedHashMap,这通常用于保持插入顺序。如果顺序不重要,HashMap可能更合适。根据具体需求选择合适的数据结构。
循环优化:尽量减少循环嵌套的深度,并优化循环逻辑,以提高效率。
清晰的注释:在代码中增加更多的注释,以解释逻辑和意图,便于他人阅读和理解代码。
单元测试:编写单元测试来确保每个功能模块的正确性,提升代码质量。
输入验证:验证输入的格式和范围,确保数据的正确性。
逻辑分离:将主方法中的逻辑分离到多个方法中,按照职责分离原则组织代码。
通过以上建议的改进,这段代码将更加健壮、可读、可维护,性能也会有所提升。
总结:
在这三个题目中我学会了ArrayList的运用,HashMap的运用,正则表达式的使用,startsWith,substring,split等,增强遍历的使用,类之间的关联和使用,要注重学会分析题目,而不是直接敲代码,对题目的设计分析远远重要于编写代码,同时要注重于代码的高效和优雅。还要把时间空出来多敲代码,最后一两天是不可能完成的。