前言
在完成這三次題目集後,我也有了一些心得和體會。關於題目集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 可以應對當前的題目需求,但如果系統規模擴大,需要使用更加複雜的資料結構和演算法來最佳化效能。在以後的時間裡,我會進一步完善我的程式碼,爭取精益求精。