南昌航空大學 22207209-侯智慧-第一次blog作業

TeFuir1發表於2024-10-26

一、前言
在過去的幾個月中,我全身心地投入到了Javapta課程的學習中,尤其是三次極具挑戰性的大作業。這些作業不僅是對我Java程式設計技能的考驗,也是我在電腦科學領域不斷探索和成長的重要里程碑。
最初接觸這些作業時,我感到有些不知所措。每個專案都要求我們運用不同的Java特性,從基本的物件導向程式設計到複雜的資料結構和演算法最佳化。面對這些挑戰,我逐漸意識到,學習程式設計不僅僅是掌握語法和工具,更是培養一種解決問題的思維方式。
在專案進行過程中,我不斷遇到各種技術難題和邏輯陷阱。有時候,一個小小的錯誤可能會花費數小時甚至數天去除錯和修復。然而,正是在這些反覆試錯和不斷迭代的過程中,我的程式設計能力得到了顯著提升。我學會了如何更有效地除錯程式碼,如何最佳化演算法以提高程式效率,以及如何在團隊協作中利用版本控制工具來管理專案。
此外,這些作業也讓我深刻體會到理論與實踐相結合的重要性。在課堂上學到的概念,只有透過實際編碼和專案應用,才能真正內化為自己的技能。每次成功解決一個棘手的問題,或者在專案中實現一個新功能,都會讓我感受到極大的成就感和滿足感。
在這篇部落格中,我將詳細分享每個作業的背景、我所採用的解決方案,以及在過程中積累的經驗和教訓。我希望這些分享不僅能幫助其他正在學習Java的同學,也能為那些對程式設計充滿好奇和熱情的人提供一些有用的啟示。程式設計之路漫漫,但每一步的努力和探索都值得我們珍惜和回味。讓我們一起繼續這段充滿挑戰的學習旅程吧!
二、設計與分析
1.第一次作業7-1:設計一個風扇Fan類
(1)難度分析:

  • 這道題目屬於入門級別,主要考察物件導向程式設計的基礎知識,包括類的定義、構造方法的使用、資料封裝、常量的定義以及方法的重寫。對於初學者來說,這道題目是一個很好的練習機會,可以幫助他們理解和掌握Java類的基本結構和功能。
    (2)知識點分析:
  • 常量的定義:
    題目要求定義三個表示風扇速度的常量(SLOW、MEDIUM、FAST),這可以透過public static final關鍵字來實現。常量是不可變的,通常在類中定義為public和static。
  • 私有資料域:
    題目要求定義幾個私有資料域,包括speed、on、radius和color。這些資料域需要透過構造方法進行初始化,並透過訪問器(getter)和修改器(setter)進行訪問和修改。
  • 構造方法:
    需要實現一個無參構造方法和一個有參構造方法。無參構造方法用於建立預設物件,而有參構造方法用於根據給定引數建立自定義物件。
  • 訪問器和修改器:
    訪問器和修改器是用於訪問和修改私有資料域的方法。它們通常被命名為getX和setX,其中X是資料域的名稱。
  • toString方法的重寫:
    toString方法用於返回物件的字串表示。需要根據風扇的狀態(開或關)返回不同的字串資訊。
  • 物件的建立和使用:
    題目要求建立兩個Fan物件,並透過呼叫toString方法顯示物件的資訊。這涉及到物件的例項化和方法呼叫。
    (3)類圖的設計

    (4)踩坑心得

  • 2.7-5 答題判題程式-1
    (1)難度分析:
  • 這道題目屬於中等難度,涉及到物件導向程式設計的多個方面,如類設計、集合操作、字串處理和輸入輸出等。
    (2)知識點分析:
  • 集合操作:
    使用List集合來儲存題目和答案,涉及到集合的新增、遍歷、排序等操作。
  • 字串處理:
    需要對輸入的字串進行解析和處理,提取題號、題目內容和答案等資訊。這涉及到字串分割、替換和修剪等操作。
    (3)類圖的設計


    (4)踩坑心得及改進建議
  • 輸入過程中沒有正確的去掉空格

    在這段程式碼中正確的處理了去掉空格的操作,利用trim()函式去空格,如果不正確的去掉空格,輸出中會有多餘的空格,導致與輸入中的標準答案不匹配,導致出錯。
    String[] parts = question.split("#"); // 使用 "#" 分隔每部分資訊利用這段函式,將題目類的所有資訊都分隔出來,以保證後續與答題類的匹配。
  • 答題資訊中沒有正確的處理"#A:"+答案內容
    正確的處理為:String[] userAnswers = input.nextLine().replaceAll("#A:", "").trim().split("\s+");
    其中.replaceAll("#A:", ""),目的是為了使用正規表示式將字串中的#A:部分替換為空字串。這一步的目的是去掉輸入中用於標識答案部分的字首#A:,以便後續處理。
    .split("\s+"),目的是為了使用正規表示式\s+來分割字串。\s+表示匹配一個或多個空白字元(包括空格、製表符、換行符等),因此該方法會將輸入字串按空白字元分割成多個部分,
    結果是一個字串陣列,其中每個元素對應一個使用者提供的答案。
    3.7-4 答題判題程式-2
    (1)難度分析
    這道題目屬於較難題目
  • 多種輸入型別的處理:
    輸入資訊包括題目資訊、試卷資訊和答卷資訊,這些資料可能會混合輸入,並且順序不定,需要程式能夠正確地解析和區分。
    解析輸入格式的複雜性增加了實現的難度,尤其是在處理格式不正確或不完整的輸入時。
  • 資料結構設計:
    需要設計合適的資料結構來儲存題目、試卷和答卷的資訊,並且這些資料結構之間要有合理的關聯。
    例如,題目和試卷之間透過題號關聯,試卷和答卷透過試卷號關聯。
  • 邏輯實現的複雜性:
    需要實現對答案的判定,並計算每道題的得分以及總分。
    還要處理特殊情況,例如試卷總分不為100的警示、答卷中多餘或缺失的答案處理等。
  • 輸出格式的複雜性:
    輸出需要根據不同的條件給出不同的資訊,這要求程式能夠靈活地生成輸出,並且格式必須嚴格符合要求。
  • 錯誤處理:
    需要處理各種可能的錯誤情況,例如輸入格式錯誤、找不到試卷號等。
    (2)知識點分析
  • 字串處理:
    使用正規表示式和字串操作方法(如split、trim、replaceAll等)來解析和處理輸入字串。
  • 集合和對映:
    使用集合(如List、Map)來儲存和管理題目、試卷和答案資訊。
    (3)類圖的設計


    (4)踩坑心得及改進建議
  • 出現所有測試點都是非零返回
    問題出現在輸入時

    修改之前的程式碼中沒有加上.trim()操作導致對於正則表達的拆解有問題,導致所有測試點非零返回。
    .trim() 的作用是去除字串兩端的空格,可以處理輸入資料中題號、內容或前後不小心包含空格的情況,避免因為這些額外的空格導致程式出錯。
  • 在輸入的處理中錯誤的使用了空格分隔
    String[] parts = line.split(" ");
    錯誤地使用了空格分割 #N 行,而 #N 行的格式是用 # 分隔欄位的。
    String[] parts = line.split("#");
    使用 # 分割,並新增了 .trim() 去除空格,確保題號、內容和標準答案解析正確。
  • 出現單試卷單答卷滿分不是100分的問題

    是因為在輸入函式InputHandler類中在建立試卷時,沒有檢查所選題目的分數總和是否等於 100 分,導致試卷滿分可能不是 100 分。
    修改意見: 在 InputHandler 類的 processQuestions 方法中,新增了對試卷總分的檢查。在建立完試卷後,計算試卷的總分,並與 100 分進行比較。如果不等於 100 分,則將警報資訊新增到 alerts 列表中。
    if (paper.getTotalScore() != 100) {
    alerts.add("alert: full score of test paper" + paperNumber + " is not 100 points");
    }
  • 出現2試卷2答卷 滿分不是100 答案有缺失 答卷試卷號不匹配的問題

    出現問題的原因是因為學生提交的答卷可能與試卷不匹配,可能答卷中指定的試卷號不存在。
    修改意見:當指定的試卷號不存在是,可以新增一個帶有空試卷的Answer物件
    修改後的程式碼:
    TestPaper paper = testPapers.get(paperNumber);
    if (paper == null) {
    System.out.println("The test paper number does not exist"); // 明確提示試卷號不存在
    // 將 Answer 物件新增到 allAnswers 列表中,即使試卷不存在,以便後續統一處理和輸出結果
    allAnswers.add(new Answer(null)); // 新增一個帶有空試卷的 Answer 物件
    return;
    }
    4.7-3 答題判題程式-3
    (1)難度分析
    這道題目是一道綜合性較高的程式設計題,涉及到多個知識點和複雜的邏輯處理。
  • 輸入格式複雜:
    題目要求處理多種不同格式的輸入資料,包括題目資訊、試卷資訊、學生資訊、答卷資訊和刪除題目資訊。這些資訊的格式各不相同,並且輸入順序可能是混亂的,這要求程式具有很強的解析能力。
  • 資料關聯性強:
    不同型別的資料之間有著緊密的關聯。例如,試卷資訊中的題目編號需要與題目資訊對應,答卷資訊中的試卷號和學號需要與試卷資訊和學生資訊對應。這種關聯性要求程式在解析和處理資料時保持一致性。
  • 多種錯誤處理:
    題目要求對多種錯誤情況進行處理,包括格式錯誤、題目引用錯誤、試卷號引用錯誤和學號引用錯誤。這需要程式能夠檢測並處理多種異常情況,並給出相應的提示資訊。
  • 多種輸出格式:
    根據不同的輸入情況,程式需要輸出不同的提示資訊和結果,包括試卷總分警示、答卷資訊、判分資訊、被刪除題目提示資訊、題目引用錯誤提示資訊等。這要求程式能夠根據不同的邏輯分支生成多種格式的輸出。
    邏輯複雜:題目涉及到的邏輯判斷較多,包括答案的正確性判斷、題目的有效性判斷、分數的計算等。這需要程式能夠處理複雜的業務邏輯。
    (2)知識點分析
  • 類和物件:
    程式碼定義了多個類:Question、Paper、Student、AnswerSheet、InPut、OutPut。這些類分別代表題目、試卷、學生、答題紙、輸入處理和輸出處理。
    每個類都有自己的屬性和方法,體現了物件導向程式設計中的封裝性。
  • 封裝:
    類的屬性通常是私有的(private),透過公共方法(public)訪問和修改這些屬性。這種封裝機制有助於保護資料不被外部直接修改。
  • 內部類:
    Paper類中定義了一個內部類QuestionScore,用於儲存題目編號和分值。這種設計有助於將相關的邏輯和資料封裝在一起。
  • 列表和對映:
    使用List(如ArrayList)來儲存題目、試卷、學生和答題紙的列表。
    使用Map(如HashMap)來儲存答案列表,題目順序號對應答案。這些資料結構有助於快速查詢和儲存資料。
  • 集合:
    使用Set(如HashSet)來記錄被刪除的題目編號,確保題目編號的唯一性。
  • 字串分割和解析:
    使用String.split()方法對輸入字串進行分割,提取出題號、題目內容、標準答案等資訊。
    使用正規表示式來驗證輸入格式的正確性。
  • 字串比較和修剪:
    使用String.trim()方法去除字串首尾的空格。
    使用String.equals()方法比較字串內容。
  • 條件判斷:
    使用if語句進行條件判斷,驗證輸入的正確性,並根據不同情況執行不同的操作。
  • 錯誤提示:
    檢測到格式錯誤時,輸出錯誤提示資訊。
  • 題目和答案的關聯:
    透過題號和順序號將題目與答案關聯起來,判斷答案的正確性並計算得分。
  • 題目刪除和失效處理:
    實現了題目刪除功能,被刪除的題目在答卷中顯示為失效,並且得分為0。
  • 試卷總分警示:
    檢查試卷的總分是否為100分,並在不滿足條件時給出警示。
  • 無效試卷和學生處理:
    檢查試卷號和學號的有效性,並在不匹配時輸出相應的提示資訊。
    (3)類圖的設計


    (4)踩坑心得及改進建議
  • 在有多個題目的時候,沒有計算每一道題的得分,例如樣例三:

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
在輸出得分的時候,錯誤輸出了:20201103 Tom:0~0,只有一道題的分數,而且我在程式碼中並沒有去詳細的記錄每一道題的得分,輸出中的兩個0全都是總得分。
改進建議:
在OutPut類的processAnswerSheet方法中,增加了scoreDetails字串來跟蹤每個問題的得分。
// 新增程式碼
StringBuilder scoreDetails = new StringBuilder();
// 修改後的迴圈部分
for (Paper.QuestionScore questionScore : answerSheet.getPaper().getQuestions()) {
int questionNumber = questionScore.getQuestionNumber();
Question question = findQuestionByNumber(questionNumber);
if (question == null) {
QuestionsresultS.add("the question " + questionNumber + " invalid~0");
scoreDetails.append("0 ");
continue;
}
String answer = answerSheet.getAnswers().get(questionNumber);
if (answer == null) {
QuestionsresultS.add("answer is null");
scoreDetails.append("0 ");
continue;
}
boolean isRight = answer.equals(question.getAnswer());
QuestionsresultS.add(question.getContent() + "~" + answer + "~" + isRight);
int score = isRight ? questionScore.getScore() : 0;
scoreDetails.append(score).append(" ");
totalScore += score;
}
// 修改後的輸出
System.out.println(answerSheet.getStudent().getStudentNumber() + " " + answerSheet.getStudent().getName() + ": " + scoreDetails.toString().trim() + "~" + totalScore);

  • 沒有意識到答案沒有輸入這個錯誤的優先順序最高
    在答卷類沒有輸入答案的情況下輸出報錯的優先順序應該是最高的,要比含錯誤格式輸入、有效刪除以及無效題目引用資訊這些錯誤的優先順序要高
    改進意見:
    答案檢查邏輯:
    在OutPut類的processAnswerSheet方法中,處理每個答題卡時,首先檢查答案是否存在:
    // 檢查答案是否存在
    String answer = answerSheet.getAnswers().getOrDefault(order, "");
    if (answer.trim().isEmpty()) {
    QuestionsresultS.add("answer is null");
    scoreDetails.append("0 ");
    continue;
    }
    這裡使用getOrDefault(order, "")來獲取答案,如果答案不存在則返回空字串。然後透過trim().isEmpty()檢查答案是否為空或只有空格。如果是,新增"answer is null"到結果列表,並繼續處理下一個問題。
    優先順序控制:
    由於這個檢查邏輯在處理每個問題的最開始進行,因此它的優先順序自然最高。即使後續有其他檢查(例如檢查題目是否被刪除或題目是否存在),這些檢查都在答案為空的情況下被跳過,因為程式碼在發現答案為空後立即continue到下一個迴圈迭代。
    邏輯順序:
    程式碼邏輯順序確保了在任何其他驗證(如題目是否被刪除、題目是否存在)之前,首先檢查答案是否為空。這種順序確保了"answer is null"的錯誤資訊在答案缺失時優先被輸出。
  • 出現部分測試點非零返回

    主要出現的問題就是無效試卷的引用。
    改進意見:
    if (answerSheet.isInvalidPaper()) {
    System.out.println("The test paper number does not exist");
    continue; // 直接跳過當前答卷的處理
    }
    if (answerSheet.isInvalidPaper() || answerSheet.isInvalidStudent()) {
    continue;
    }
    當檢測到試卷或學生無效時,立即使用continue語句跳過當前答卷的進一步處理。這避免了程式試圖處理無效資料時可能出現的錯誤。
  • 沒有分清楚試卷號和順序號
    格式約束:
    答案數量可以不等於試卷資訊中題目的數量,沒有答案的題目計0分,多餘的答案直接忽略,答案之間以英文空格分隔。
    答案內容可以為空,即””。
    答案內容中如果首尾有多餘的空格,應去除後再進行判斷。
    樣例:
    #T:1 1-5 3-2 2-5 6-9 4-10 7-3
    #S:1 20201103 #A:2-5 #A:6-4
    1是試卷號
    20201103是學號
    2-5中的2是試卷中順序號,5是試卷第2題的答案,即T中3-2的答案
    6-4中的6是試卷中順序號,4是試卷第6題的答案,即T中7-3的答案
    注意:不要混淆順序號與題號
    int order = paper.getQuestions().indexOf(questionScore) + 1;
    設定一個order來存順序號。
    String answer = answerSheet.getAnswers().getOrDefault(order, "");
    if (answer.trim().isEmpty()) {
    QuestionsresultS.add("answer is null");
    scoreDetails.append("0 ");
    continue;
    }
    檢查答案是否存在,根據上面的樣例可以看到答案類的去都是順序號。
    三、總結
    在過去的幾個月中,深入學習Java程式設計的過程中,我經歷了從基礎到複雜的多個專案挑戰。這些專案不僅讓我掌握了Java的語法和特性,還培養了我解決問題的能力和程式設計思維。
    透過第一次作業,我鞏固了物件導向程式設計的基礎知識,理解了類的定義、構造方法、資料封裝、常量定義和方法重寫的重要性。這為我在後續專案中設計和實現複雜的類結構奠定了基礎。
    隨著作業難度的增加,我開始接觸到集合操作、字串處理和輸入輸出等中級知識點。這些技能在第二次作業中得到了充分的應用。我學會了如何有效地解析和處理輸入資料,特別是在應對格式複雜的輸入時,如何利用正規表示式和字串操作來提取有用的資訊。
    第三次作業則是對我程式設計能力的全面考驗。面對多種輸入型別、複雜的資料結構設計和多樣的錯誤處理,我逐步提高了程式碼的健壯性和可維護性。我認識到,良好的程式碼結構和清晰的邏輯是解決複雜問題的關鍵。
    在整個學習過程中,我也積累了許多經驗和教訓。比如,正確處理輸入中的空格和格式問題,確保程式的輸出與預期一致;在設計資料結構時,保持資料之間的關聯性和一致性;在處理錯誤時,優先處理那些對程式影響最大的錯誤。
    這些專案不僅提高了我的程式設計技術,也讓我更加理解理論與實踐相結合的重要性。每次解決一個難題或實現一個新功能,都會帶給我極大的成就感和滿足感。程式設計之路充滿挑戰,但每一次努力和探索都值得珍惜。未來,我將繼續這段充滿挑戰的學習旅程,不斷提升自己的技能和知識水平。

相關文章