前三次作業總結

李泽月發表於2024-10-26
  • 前言

  • 第一題知識點
    1.基礎Java語法與資料型別
    int: 基本資料型別,用於儲存整數,例如 numOfQuestions 是題目數量。
    String: 物件型別,用於處理字串資料。所有輸入和輸出的資料處理都涉及到字串處理,例如 line 表示從使用者輸入讀取的一行資料。
    2. 輸入輸出
    Scanner類:
    Scanner scanner = new Scanner(System.in); 用於讀取使用者輸入。System.in 代表標準輸入(通常是控制檯)。
    scanner.nextLine() 讀取整行輸入資料,通常以 \n 作為結束符。
    System.out.println():
    用於列印輸出。該程式用來顯示題目內容和使用者的答案,以及最終的判題結果。
    3. 集合框架
    Map介面與HashMap類:
    Map<Integer, String> 用來儲存鍵值對 (key-value),此處的 Integer 是題號,String 是題目。
    HashMap 是 Map 的實現類,提供了 put() 方法來插入資料、get() 方法來獲取資料。
    questionMap.put(questionNumber, questionContent) 將題號和題目內容存入 questionMap。
    List介面與ArrayList類:
    List 用於儲存順序資料,如使用者的答案和判題結果。
    ArrayList 是 List 的實現類,可以動態調整大小。
    userAnswers.add(ans.substring(3)) 用來去掉 #A: 標記並將使用者答案存入 userAnswers 列表。
    4. 字串操作
    String.indexOf(String str):
    返回指定子字串 str 在字串中首次出現的索引。如果不存在則返回 -1。
    用於定位 #N:, #Q:, #A: 的位置,例如 line.indexOf("#N:") + 3 用於獲取題號的開始位置。
    String.substring(int beginIndex, int endIndex):
    返回從 beginIndex 到 endIndex 之間的子字串(不包含 endIndex 字元)。
    用來提取題號、題目和正確**。
    String.trim():
    去除字串兩端的空白字元。
    常用於清理使用者輸入資料,如 scanner.nextLine().trim()。
    5. 迴圈與條件判斷
    for 迴圈:
    用於迴圈處理題目的讀取和輸出。
    for (int i = 0; i < numOfQuestions; i++) 遍歷題目輸入。
    while 迴圈:
    用於逐行讀取使用者的答題資訊,直到輸入 end 為止。
    while (!answerLine.equals("end")) 檢查使用者輸入是否為 end。
    if-else 條件語句:
    用來判斷使用者答案是否正確。
    if (answerMap.get(questionNumber).equals(userAnswer)) 判斷使用者答案和正確答案是否相等。
    6. 排序
    Collections.sort(List list):
    該方法用於對列表進行排序。程式對 questionMap 的鍵列表進行排序,以確保按題號順序輸出。
    Collections.sort(sortedKeys) 將題號列表按從小到大的順序排序。
    7. Java中的錯誤處理
    程式中使用了 Integer.parseInt() 方法來將字串轉換為整數。如果使用者輸入的題號不是有效的整數,這裡可能會丟擲 NumberFormatException 異常。雖然程式碼裡沒有專門的異常處理部分,但在實際應用中,可以考慮使用 try-catch 塊來捕獲和處理這些異常,確保程式的健壯性。
    8. 資料讀取和解析
    程式碼展示瞭如何處理格式化輸入。比如: #N:1#Q:What is 2+2?#A:4,這種格式化的輸入需要使用字串操作技巧來提取不同部分。
    這種字串解析技巧是處理文字資料時非常常見的程式設計模式。
    9. ArrayList的用法
    add(element): 將元素新增到列表末尾。
    get(index): 獲取指定索引處的元素。
    size(): 獲取列表的大小。

  • 第二題知識點

  1. 類與物件
    類的定義:Question、TestPaper、AnswerSheet三個類分別封裝了題目、試卷和答題卡的資料和行為。
    構造方法:每個類都定義了構造方法(Question(int id, String content, String answer)等),用於物件的初始化。構造方法在建立物件時自動呼叫,確保物件的初始狀態正確。
    屬性的封裝:每個類使用公有屬性來儲存資料。為了更好的設計,屬性一般設定為private並透過getter和setter訪問,但此處屬性為public用於簡化示例程式碼。
  2. 集合框架
    Map介面及其實現類:HashMap用於儲存題目,LinkedHashMap用於儲存試卷的題目-分數對映。
    HashMap實現基於雜湊表,提供O(1)的查詢效率。題目列表(questionMap)透過HashMap按題目ID快速查詢。
    LinkedHashMap在TestPaper類中用於保持題目新增的順序。LinkedHashMap保留插入順序,便於控制輸出順序。
    List介面及其實現類:ArrayList用於儲存考生的**(answers)。
    ArrayList是一種可動態擴充套件的陣列,適合儲存有序的、可變數量的物件,如考生的答題順序。
  3. 字串處理
    split方法:利用String類的split方法解析輸入字串,根據不同的識別符號提取資料。
    substring方法:substring用於擷取字串中的特定部分(如#T:123獲取試卷ID 123)。
    字串比較:利用String.equals方法來檢查考生**與正確答案是否匹配。
  4. 控制結構
    迴圈結構:for和while迴圈用於遍歷集合和讀取輸入。
    for-each迴圈用於遍歷集合元素,如for (AnswerSheet sheet : answerSheets)。
    條件判斷:if語句用於檢查輸入型別,驗證答題的正確性等。
    跳出迴圈:break用於終止while迴圈,當使用者輸入“end”時退出。
  5. 標準輸入和輸出
    Scanner類:Scanner用於從控制檯讀取輸入,結合nextLine()方法逐行讀取資料。
    標準輸出:利用System.out.println輸出處理結果、分數、答題正確性提示等資訊。
  • 第三題知識點
    1. 類的設計
    Question類:題目類,包含id(題目ID)、content(題目內容)和answer(題目正確答案)三個屬性。透過構造方法Question(int id, String content, String answer)來初始化一個Question物件。
    TestPaper類:試卷類,用來管理一個試卷上的題目及對應分數,包含:
    id:試卷的ID。
    questions:一個Map<Integer, Integer>,用來儲存題目ID及其對應的分數。這裡Map的鍵值對結構很適合題目與分數的繫結關係。
    totalScore:記錄試卷的總分,透過addQuestion(int qId, int score)方法將題目新增到試卷並更新總分。
    checkFullScore()方法:檢查試卷的滿分是否是100分。如果不是滿分100分,後續會輸出警告資訊。
    AnswerSheet類:答卷類,用來管理學生的答卷資訊,包含:
    testPaperId:答卷所屬試卷的ID。
    studentId:學生的ID。
    answers:儲存每道題目對應的學生答案。方法addAnswer(String answer)將學生的答案新增到answers列表中。
    Student類:學生類,記錄學生的基本資訊。包含學生的id(學號)和name(姓名),用於標識每個學生。
    Main類:主類,透過main方法執行整個程式的控制邏輯,包括從輸入獲取資料、處理各種資料型別(題目、試卷、答卷等),並完成最終的答卷評分和輸出。
    2. 資料結構的使用
    Map介面(HashMap和LinkedHashMap):
    questionMap:用於儲存題目,鍵是題目ID,值是Question物件。
    testPaperMap:用於儲存試卷,鍵是試卷ID,值是TestPaper物件。
    studentMap:用於儲存學生,鍵是學生ID,值是Student物件。
    LinkedHashMap用於TestPaper類中的questions欄位,因為它可以保持插入的順序,確保題目在答卷中的順序正確。
    List介面(ArrayList):用於儲存按順序排列的資料:
    answerSheets:儲存所有學生的答卷AnswerSheet物件。
    answers:AnswerSheet類中儲存學生每個題目的答案。
    Set介面(HashSet):deletedQuestions用於儲存已刪除的題目ID。HashSet的查詢效率較高,這樣在驗證某個題目是否被刪除時會更快速。
    3. 輸入輸出和資料解析
    輸入處理:
    使用Scanner從標準輸入逐行讀取資料。sc.nextLine()讀取一行字串,trim()去除兩端的空白,input.startsWith("#N:")檢查輸入字首以確定行的型別。
    透過字串方法split()解析不同輸入模式:
    "#N:"開頭的行用於定義題目,解析出題目ID、內容、答案後,將題目存入questionMap。
    "#T:"開頭的行用於定義試卷,將題目和分數解析後新增到試卷TestPaper物件中。
    "#S:"開頭的行用於定義答卷,解析答卷的試卷ID、學生ID、學生答案。
    "#X:"開頭的行用於定義學生,將學生ID和姓名解析後存入studentMap。
    "#D:N-"開頭的行表示刪除某個題目,記錄在deletedQuestions中。

  • 這幾次的題量稍大,如果不留足充裕的時間,很難答完所有題目並透過所有測試點,這三次的題目難度隨著每一次更迭,難度在逐漸增大,第一次比較簡單,能夠很快完成,第二次難度稍微增大需要投入更多的時間和精力去完成,第三次難度較大,需要花費很多時間和精力去完成。

  • 第一題設計分析
    1.題目定義了清晰的輸入格式,分為三部分:
    題目數量:代表後續題目數的整數,最高位不能為0(也就是說數字不應以0開頭)。
    題目內容:
    格式為 #N:<題號> #Q:<題目內容> #A:<標準答案>。
    題號與題目輸入順序無關,意味著我們可能需要將題號與題目內容、答案進行關聯儲存,之後按題號順序排序輸出。
    答題資訊:
    答題資訊按行輸入,以 #A:<答案內容> 格式儲存。
    每行的答題資料對應每道題目,順序與題號對應(題號按升序排列後答案與題目內容的順序一致)。
    end 標記答題資訊輸入結束。
    2.輸出結構分析
    程式的輸出分為三部分:
    題目數量:直接輸出已讀取的題目數量。
    題目內容及使用者答案:輸出題目內容和使用者答案(格式 題目內容~答案),按題號從小到大的順序。
    判題資訊:根據使用者的答案判斷正誤,並輸出 true 或 false(順序與輸入答題資訊的順序一致)。
    3.程式設計與資料結構
    根據題目的要求,我們可以設計以下資料結構來儲存題目和答案資訊:
    題目資訊儲存結構
    使用 Map<Integer, String> 來儲存題目編號與題目內容,便於透過題號來儲存和查詢。
    使用 Map<Integer, String> 來儲存題目編號與標準答案。
    使用者答案與判題資訊儲存
    使用 List 儲存使用者的答題資訊,逐條新增使用者的答案。
    使用 List 或 List 儲存判題結果,最終輸出 true 或 false。
    4.具體步驟設計
    以下是按題目要求拆解的詳細程式流程:
    (1. 讀取輸入部分
    讀取題目數量:讀取並解析第一行,獲取題目數量 numOfQuestions。
    讀取題目內容:
    迴圈讀取接下來的 numOfQuestions 行,每行包含題號、題目內容和標準答案。
    使用字串分割、索引定位(#N:, #Q:, #A:)提取題號、題目內容和標準答案。
    將題號與題目內容、標準答案分別存入 questionMap 和 answerMap 中。
    讀取使用者答題資訊:
    使用 while 迴圈讀取答題資訊行,直到 end。
    每行將 #A:<答案內容> 格式的資料提取成單獨答案,去掉字首 #A:。
    將使用者的答案按順序存入 userAnswers 列表。
    (2. 處理與輸出部分
    輸出題目數量:直接輸出 numOfQuestions。
    輸出題目內容及使用者答案:
    按題號從小到大排序 questionMap 的鍵。
    根據排序後的題號順序逐一輸出題目內容和使用者的答案,格式為 題目內容~使用者答案。
    判題:
    遍歷題號列表,逐一將使用者的答案和標準答案比對,若相等則判為 true,否則判為 false。
    將判題結果存入 results 列表,最終輸出結果。

  • 第一題改進建議

  1. 題目資訊的輸入與解析
點選檢視程式碼
Map<Integer, String> questionMap = new HashMap<>(); // 題號 -> 題目
Map<Integer, String> answerMap = new HashMap<>();   // 題號 -> 正確答案

for (int i = 0; i < numOfQuestions; i++) {
    String line = scanner.nextLine().trim();

    // 解析題號、題目內容和正確答案
    int nIndex = line.indexOf("#N:") + 3;
    int qIndex = line.indexOf("#Q:");
    int aIndex = line.indexOf("#A:");

    int questionNumber = Integer.parseInt(line.substring(nIndex, qIndex).trim());
    String questionContent = line.substring(qIndex + 3, aIndex).trim();
    String correctAnswer = line.substring(aIndex + 3).trim();

    // 將題號和題目存入 questionMap,題號和正確答案存入 answerMap
    questionMap.put(questionNumber, questionContent);
    answerMap.put(questionNumber, correctAnswer);
}

功能:逐行讀取題目資訊,包括題號、題目內容和標準答案,並將它們分別存入 questionMap 和 answerMap。 邏輯: 透過 indexOf() 和 substring() 方法解析每一行的題目資訊: nIndex、qIndex、aIndex 用於定位 #N:、#Q:、#A: 標記的位置。 questionNumber 從 #N: 到 #Q: 之間的子字串中解析出來。 questionContent 是 #Q: 到 #A: 之間的內容。 correctAnswer 則是 #A: 之後的字串內容。 questionMap.put(questionNumber, questionContent) 和 answerMap.put(questionNumber, correctAnswer) 分別將題目和答案存入 Map。 改進建議: 資料完整性校驗:檢查 line 是否含有 #N:、#Q:、#A: 等標記,如果缺少其中之一,可以直接跳過該行或提示錯誤。 異常處理:使用 try-catch 捕獲 Integer.parseInt() 可能的異常,例如題號不是整數的情況。 題號唯一性:如果同一題號出現多次,可以考慮覆蓋或提示使用者檢查重複。 2答題資訊的輸入
點選檢視程式碼
String answerLine = scanner.nextLine().trim();
List<String> userAnswers = new ArrayList<>();
while (!answerLine.equals("end")) {
    String[] answers = answerLine.split("\\s+");
    for (String ans : answers) {
        userAnswers.add(ans.substring(3)); // 移除 "#A:" 部分,保留答案內容
    }
    answerLine = scanner.nextLine().trim(); // 繼續讀取下一行直到 "end"
}

功能:讀取使用者的答題資訊,直到遇到 "end" 關鍵字為止。 邏輯: while (!answerLine.equals("end")) 用於迴圈讀取使用者答題資訊,直到輸入結束標記 end。 answerLine.split("\\s+") 根據空白字元將一行分割成一個個答案。 ans.substring(3) 去掉 #A: 字首,保留使用者提供的答案內容,並新增到 userAnswers 列表。 改進建議: 格式校驗:檢查 #A: 字首是否存在,避免無字首或格式錯誤導致的異常。 答案數量匹配:在結束輸入後檢查 userAnswers 的數量是否與 numOfQuestions 相符,如果不相符可以給出提示或處理方案。 3.輸出題目內容及使用者答案,並判題
點選檢視程式碼
List<Integer> sortedKeys = new ArrayList<>(questionMap.keySet());
Collections.sort(sortedKeys);  // 按題號順序輸出題目

List<String> results = new ArrayList<>();
for (int i = 0; i < sortedKeys.size(); i++) {
    int questionNumber = sortedKeys.get(i);
    String questionContent = questionMap.get(questionNumber);
    String userAnswer = userAnswers.get(i);

    // 輸出格式:題目內容+" ~"+使用者的答案
    System.out.println(questionContent + "~" + userAnswer);

    // 判斷答案是否正確,儲存判題結果
    if (answerMap.get(questionNumber).equals(userAnswer)) {
        results.add("true");
    } else {
        results.add("false");
    }
}

功能:按題號順序輸出每道題的內容和使用者的答案,並將判題結果儲存到 results 列表中。 邏輯: Collections.sort(sortedKeys) 按題號升序排序題目。 使用 for 迴圈逐一輸出題目內容和使用者的答案(格式為 題目內容 ~ 使用者答案),並檢查使用者的答案是否正確: answerMap.get(questionNumber).equals(userAnswer) 比對標準答案與使用者答案。 比對結果存入 results 列表,用於之後輸出。 改進建議: 異常處理:若 userAnswers 列表的長度與 sortedKeys 長度不符,可能會引發 IndexOutOfBoundsException,在判題前可以驗證兩者是否相等。 精細輸出控制:輸出內容與題目要求一致,即題目內容和答案之間用 "~" 分隔。
  • 第二題設計與分析
    1.程式設計了三個類來對應考試系統的不同組成部分:
    Question類:包含題目ID、內容和正確答案。這是一個簡單的模型類,用於儲存題目的基本資訊。
    TestPaper類:代表試卷物件,包含試卷ID、題目-分數的對映關係,以及試卷的總分。該類可以實現試卷和題目之間的關係對映,利用了Java的Map資料結構。
    AnswerSheet類:表示考生的答題卡,包含考生使用的試卷ID和考生作答的答案列表。
  1. Java集合和資料結構
    Map介面和實現類:HashMap和LinkedHashMap用於儲存和對映關係。HashMap儲存了所有題目資訊(questionMap),並根據題目ID快速查詢題目資訊;LinkedHashMap在TestPaper類中用於保持題目新增的順序(因為順序會影響輸出結果的順序)。
    ArrayList:在AnswerSheet類中使用List儲存考生的答案順序,以便逐題對比和得分計算。
    Scanner類:透過Scanner類讀取使用者從控制檯輸入的資料。逐行讀取、解析並儲存在適當的資料結構中。
  2. 字串解析與處理
    ‘#N:開頭表示題目資訊,格式解析為題目ID、內容和正確答案。
    ‘#T:開頭表示試卷資訊,解析試卷ID和題目-分數對。
    ‘S:開頭表示答題卡資訊,解析試卷ID和考生的各個答案。
    字串擷取:透過substring方法提取字串中的子字串。例如從#T:字串中獲取試卷ID。
  3. 基礎演算法與邏輯控制
    總分驗證:TestPaper類中提供了checkFullScore方法,用於驗證試卷的總分是否達到100分。主程式在處理完所有試卷後,檢查所有試卷的總分是否正確,並輸出提示資訊。
    答案匹配與得分計算:在主程式中遍歷答題卡的答案,並將其與對應題目的正確答案進行比對。對於每個答題:
    如果考生答案正確,則記錄該題的分數;否則記錄為0分。
    累加每題的得分來計算考生的總得分。
  4. 異常與特殊情況處理
    試卷總分不達標提示:輸出提醒,如果某份試卷的總分不等於100分,顯示提醒資訊。
    無效答題卡處理:若考生答題卡上的試卷ID在系統中不存在,程式會提示“試卷號不存在”。
    缺失答案處理:若考生答題卡缺少題目答案時,輸出“answer is null”,並記錄該題得分為0。
    題目資訊缺失:在答案對比時,若沒有找到對應的題目資訊(即questionMap中沒有該題ID),同樣記錄為0分。
  • 第二題改進建議
    1.Question 類
點選檢視程式碼
class Question {
    int id;
    String content;
    String answer;

    public Question(int id, String content, String answer) {
        this.id = id;
        this.content = content;
        this.answer = answer;
    }
}

功能與實現 功能:儲存試題的相關資訊,包括題目編號id、題目內容content、正確答案answer。 實現:建構函式初始化這些屬性,使得每個Question物件都包含一個試題的完整資訊。 關鍵邏輯與改進建議 邏輯簡潔:每個題目被唯一識別符號id標識,可以透過id方便地從對映中獲取題目內容及答案。 改進建議: 增加題目屬性的訪問方法(如getId()、getAnswer()),避免直接訪問屬性,使類封裝性更強。 可以增加一個isCorrectAnswer(String userAnswer)方法,封裝答案比對邏輯。 2.TestPaper 類
點選檢視程式碼
class TestPaper {
    int id;
    Map<Integer, Integer> questions = new LinkedHashMap<>();
    int totalScore = 0;

    public TestPaper(int id) {
        this.id = id;
    }

    public void addQuestion(int qId, int score) {
        questions.put(qId, score);
        totalScore += score;
    }

    public boolean checkFullScore() {
        return totalScore == 100;
    }
}

功能與實現 功能:表示一份試卷,包含試卷編號、題目和題目分數的對映、試卷總分。 實現:透過addQuestion方法將題目新增到questions對映,且自動累加總分。checkFullScore方法用於檢查總分是否為100。 關鍵邏輯與改進建議 關鍵邏輯: 試卷中的題目和分數用LinkedHashMap儲存,確保題目按插入順序輸出,便於逐題比對和計分。 總分檢查邏輯實現簡單,但硬編碼為100分,限制了靈活性。 改進建議: 改為動態配置總分:可以將總分值作為構造引數傳入,增強適用性(例如設定不同型別的試卷總分)。 題目編號校驗:在addQuestion中加入重複qId檢測,避免誤新增同一題目。 封裝題目獲取方法:增加getQuestions()等方法,以進一步保護內部資料結構。 3.AnswerSheet 類
點選檢視程式碼
class AnswerSheet {
    int testPaperId;
    List<String> answers = new ArrayList<>();

    public AnswerSheet(int testPaperId) {
        this.testPaperId = testPaperId;
    }

    public void addAnswer(String answer) {
        answers.add(answer);
    }
}

功能與實現 功能:表示一張答題卡,包含對應的試卷編號和考生答案列表。 實現:透過addAnswer方法,將答案逐一新增到answers列表中。 關鍵邏輯與改進建議 邏輯合理性:用List儲存考生的**順序,方便後續按試卷順序逐題比對。 改進建議: 封裝答案獲取方法:增加getAnswers()方法,避免直接訪問屬性answers。 答案數量檢查:可以在新增答案時檢查試卷中的題目數量,提前處理少答、多答情況。 增加標識考生ID:如果要支援多張答題卡的比較,可以考慮增加考生ID屬性以唯一標識答題卡。
  • 第三題設計與分析
    1. 類的設計
    該程式由多個類構成,每個類承擔特定的職責,透過組合使用實現了較為清晰的層次結構。類之間的資料和功能分離,充分體現了物件導向的設計原則。
    2. Question 類
    職責:代表考試系統中的一道題目,包含題目內容、答案和唯一識別符號(題目ID)。
    屬性:
    id:題目ID,型別為int,用於唯一標識一道題目。
    content:題目內容,型別為String。
    answer:題目的正確答案,型別為String。
    設計分析:
    透過構造器 Question(int id, String content, String answer) 初始化每道題目的ID、內容和答案。
    由於類中沒有複雜的邏輯,僅用於儲存資料,因此沒有額外方法,實現簡單直接。
    3.TestPaper 類
    職責:代表一份試卷,包含多個題目及每個題目的分數,並提供檢測總分是否滿足滿分要求的功能。
    屬性:
    id:試卷ID,型別為int。
    questions:儲存題目ID及對應分數的Map<Integer, Integer>,鍵為題目ID,值為分數,使用LinkedHashMap來確保題目的插入順序。
    totalScore:試卷的總分數,型別為int,動態更新每道題目分數累加的總和。
    方法:
    addQuestion(int qId, int score):新增題目及分數到試卷中,並累加總分。
    checkFullScore():檢查試卷的滿分是否為100,若不滿足則可以發出警告。
    設計分析:
    試卷類以Map儲存題目ID與分數,方便查詢題目分數。使用LinkedHashMap確保按題目新增順序儲存,方便後續與答卷對應。
    滿分檢查功能分離到checkFullScore()方法中,使程式碼更具可讀性。
    4. AnswerSheet 類
    職責:記錄學生的答卷,包含答卷對應的試卷ID、學生ID及題目的作答情況。
    屬性:
    testPaperId:試卷ID,型別為int。
    studentId:學生ID,型別為String,用於標識答卷歸屬的學生。
    answers:儲存學生每題答案的List,按題目順序儲存。
    方法:
    addAnswer(String answer):將一個題目的答案新增到answers列表。
    設計分析:
    AnswerSheet類儲存答卷的學生ID、試卷ID及答案,且答案列表與試卷的題目列表對應。利用addAnswer方法將答案逐題新增,確保順序一致。
    5. Student 類
    職責:代表學生的基本資訊,包含學生ID和姓名。
    屬性:
    id:學生ID,型別為String。
    name:學生姓名,型別為String。
    設計分析:
    Student類僅儲存學生的基本資訊,並透過構造器初始化。設計簡單,但在程式中透過學生ID關聯答卷和學生。
    6. Main 類
    職責:程式的入口,控制資料的輸入、處理和輸出。
    主要功能:
    初始化和維護題目、試卷、答卷和學生的集合。
    控制資料輸入和解析,處理資料異常,驗證資料的合法性。
    執行評分流程,輸出每個學生的答題結果和總分。
    7. 資料結構分析
    HashMap:用於儲存題目、試卷、學生資料,查詢效能優異。因為題目、學生和試卷ID在整個系統中都是唯一的,這些資料適合用HashMap儲存。
    LinkedHashMap:用於TestPaper類中的questions,它能保證題目在試卷中按新增順序排列。
    ArrayList:用於儲存答卷中的答案列表,按順序新增答案,易於與題目列表一一對應。
    HashSet:用於儲存被刪除題目的ID,用於快速查詢判斷題目是否已被刪除。

  • 第三題改進建議
    1.Question 類

點選檢視程式碼
class Question {
    int id;
    String content;
    String answer;

    public Question(int id, String content, String answer) {
        this.id = id;
        this.content = content;
        this.answer = answer;
    }
}

關鍵邏輯: 構造方法 Question(int id, String content, String answer) 初始化題目ID、內容和正確答案。 該類主要用於儲存題目屬性,沒有複雜的邏輯操作。 改進建議: 可以擴充套件屬性,比如增加題型(選擇題、填空題等)來支援不同題型。 在初始化時增加輸入驗證,比如檢查答案是否為空或格式是否符合要求,以確保資料完整性。 2.TestPaper 類
點選檢視程式碼
class TestPaper {
    int id;
    Map<Integer, Integer> questions = new LinkedHashMap<>();
    int totalScore = 0;

    public TestPaper(int id) {
        this.id = id;
    }

    public void addQuestion(int qId, int score) {
        questions.put(qId, score);
        totalScore += score;
    }

    public boolean checkFullScore() {
        return totalScore == 100;
    }
}

關鍵邏輯: addQuestion(int qId, int score) 方法新增題目到 questions,並累加該題目分值到 totalScore,以便後續驗證是否滿分。 checkFullScore() 方法用於檢查試卷的總分是否為100分,返回布林值來標識。 改進建議: 可增加 removeQuestion 方法,用於從試卷中移除題目及分數,並更新 totalScore,避免題目被刪除後無法從試卷分數中減去。 還可增加更多分數驗證邏輯,如確保每題分數合理,且新增題目時總分不會超過100分。 3.AnswerSheet 類
點選檢視程式碼
class AnswerSheet {
    int testPaperId;
    String studentId;
    List<String> answers = new ArrayList<>();

    public AnswerSheet(int testPaperId, String studentId) {
        this.testPaperId = testPaperId;
        this.studentId = studentId;
    }

    public void addAnswer(String answer) {
        answers.add(answer);
    }
}

關鍵邏輯: 構造方法初始化試卷ID、學生ID。 addAnswer(String answer) 方法將學生的每個答案記錄在 answers 列表中。列表結構允許記錄學生按順序提交的多個答案。 改進建議: 可以增加一個屬性 isSubmitted 標記答卷是否最終提交。 增加答案校驗,比如檢查答案是否為空、長度限制等,從而保證資料的有效性。 若支援多種題型,可進一步擴充套件該類的答案格式,以應對不同題型的答案結構。
  • 踩坑心得
    1. 輸入資料格式不規範
    坑:題目內容和答題資訊的格式不符合規範,如沒有指定字首(#N:、#Q:、#A:),或輸入內容缺少某些欄位。比如,題目描述的格式是 #N:1 #Q:1+1= #A:2,而使用者可能輸入 #N:1 #Q:1+1= 2。
    解決辦法:在解析字串時,首先檢查格式的完整性。例如,確保字串包含所有需要的標記符並且順序正確。如果不符合格式要求,可以跳過該行或輸出提示資訊告知使用者輸入格式有誤。
    心得:格式檢查是確保程式健壯性的第一步,特別是在處理使用者輸入時要儘可能避免假設輸入總是正確的。
    2. 題號順序與輸入順序不一致
    坑:題號的順序不一定是按順序輸入的,因此直接按輸入順序儲存並輸出會導致題目錯位。
    解決辦法:將題目存入 Map 中,以題號作為鍵,然後在輸出時將題號排序,以保證輸出的題目順序正確。
    心得:在程式邏輯上儘量減少對“順序性”輸入的依賴,使用 Map 這種鍵值結構可以更靈活地處理題目順序問題。
    3. 題號重複
    坑:如果輸入中出現重複的題號,程式直接儲存會導致覆蓋之前的題目資訊或答案,導致最終輸出結果錯誤。
    解決辦法:在插入 Map 之前檢查是否已包含該題號。如果題號重複,可以選擇跳過或記錄並告知使用者重複。
    心得:提前識別重複資料有助於提升程式的可靠性,特別是題號這樣的關鍵欄位應保持唯一性。
    4. 題目數量和答案數量不匹配
    坑:如果使用者輸入的答題數量少於或多於題目數量,直接處理會導致 IndexOutOfBoundsException 或漏判部分題目。
    解決辦法:在處理答題資訊之前,檢查答題的數量是否與題目數量一致。若不一致,可以在程式結束時給出提示。
    心得:對於數量不匹配的情況,及時給出反饋或提示可以幫助使用者快速發現輸入中的問題,而不是等到程式出錯才發現。
    5. 字串操作錯誤
    坑:使用 indexOf() 和 substring() 等方法提取題目和答案時,若標記符未找到,會返回 -1,進而導致 substring() 產生異常。
    解決辦法:在執行字串操作前,先判斷標記符的位置是否有效。例如,如果 line.indexOf("#Q:") == -1,則說明題目內容缺失,應跳過該行。
    心得:字串解析要特別小心,特別是使用 indexOf() 和 substring() 時,確保位置合法性,避免處理異常情況時出錯。
    6. 不合理的異常處理
    坑:如果沒有捕獲可能的異常,或捕獲異常後沒有詳細的錯誤資訊提示,程式會在執行時崩潰,使用者可能不知道原因。
    解決辦法:使用 try-catch 捕獲解析和型別轉換中的常見異常,如 NumberFormatException 和 IndexOutOfBoundsException,並輸出具體的錯誤資訊,讓使用者瞭解問題所在。
    心得:良好的異常處理不僅有助於除錯,還能提供使用者友好的反饋,避免程式在輸入不合規時直接崩潰。
    7. 答案判定中的大小寫或空格問題
    坑:在判定答案的正確性時,字串的大小寫或多餘空格可能導致答案被錯誤地判斷為不正確。
    解決辦法:在判題時使用 trim() 去除空格,或使用 equalsIgnoreCase() 進行大小寫不敏感的比較(如果題目允許)。
    心得:使用者輸入的答案可能不嚴格遵循大小寫格式,適當的預處理會讓程式對使用者更友好。
    8. 題號排序與答案輸出順序對不上
    坑:題號的排序和答題資訊的順序不匹配,導致答案輸出和題目內容不對應。
    解決辦法:題號排序後,按題號順序依次匹配答案並輸出。可以用 List 將題號排序後再按順序輸出。
    心得:保證題目與答案的對應關係,特別是在輸出時,確保順序一致性會極大地提升使用者的使用體驗。
    9. Scanner 輸入處理中的“殘餘換行”問題
    坑:使用 nextInt() 讀取數字後,再使用 nextLine() 會讀取到換行符,導致資料不完整。
    解決辦法:儘量使用 nextLine(),然後用 Integer.parseInt() 將字串轉為整數,以避免遺留的換行符干擾。
    心得:Scanner 的輸入方法之間有時會產生不必要的“行殘留”,建議保持輸入方式的一致性,避免遺漏重要資料。
    10. 使用不恰當的資料結構
    坑:如果直接使用 List 或陣列來儲存題目和答案,不僅在查詢和排序上更麻煩,還容易出現順序錯位。
    解決辦法:合理選擇 Map 或 List 結構。Map 適合題號和題目資訊的對映儲存,而 List 則適合順序資料如使用者答題記錄。
    心得:在題目和答案對照、順序輸出等任務中,適當的資料結構選擇可以減少不必要的複雜度,提升程式碼的簡潔性。

  • 總結
    1.學到的內容
    物件導向設計:程式碼透過Question、TestPaper、AnswerSheet和Student等類來封裝資料結構,體現了物件導向設計思想,有助於模組化和程式碼複用。
    集合和資料結構的靈活應用:程式碼中大量使用了Map、List和Set來管理題目、試卷、答卷和學生資訊,並高效地進行資料檢索和刪除操作。
    資料解析和輸入驗證:透過Scanner讀取輸入,使用字串解析和異常處理來識別不同型別的資料結構,增強了程式碼的健壯性。
    條件分支和異常處理:利用try-catch塊和if-else邏輯分支來捕捉並處理潛在的輸入錯誤,確保系統在處理異常輸入時不崩潰。
    2.需要進一步學習和研究的方面
    異常處理改進:當前程式碼僅在輸入解析階段進行簡單的格式驗證,後續需要對資料缺失(如題目不存在、答案為空等)做更精細的異常處理,提升系統的穩健性。
    程式碼最佳化:可以將評分過程和輸入解析過程模組化,提取到單獨的方法中,減少main方法的複雜度,增強程式碼的可讀性和可維護性。
    單元測試:引入JUnit等測試框架,對各個模組進行單元測試,以驗證輸入解析、評分等功能的正確性,確保系統的可靠性。
    資料持久化:當前程式碼僅使用記憶體資料結構來管理題目、試卷和答卷,進一步可以探索資料持久化(如資料庫儲存)以便在多次執行中保持資料狀態。
    擴充套件性:可以考慮增加更多的功能,例如記錄答卷提交時間、支援批次刪除題目、實現多份試卷的分數統計等,以提升系統的靈活性和實用性。