一:前言:
1.知識點:主要包括類和物件的使用、資料封裝、方法的定義和使用、繼承、多型、泛型、抽象類,集合框架,異常處理,字串處理、以及基本的輸入輸出操作,每次題集的最後一題對於字串的處理的要求都比較細緻,有很多需要考慮的細節,這部分在後面詳細介紹。
2.題量:現在的題目集都是一道題了,題量沒什麼說的。
3.難度:每次題集的難度都較大,每次都要7.8小時及以上,特別是第六次的電路題,大概寫了10個小時,而且這次測試點還沒提示了。第四次的題集主要是新增了單選題,多選題,填空題,難度適中,但是最後一個測試點對空格的要求太嚴格了,只刪除末尾空格中的一個,很難想的到。第五次的題集是新開的電路模擬程式,由於是第一次需要考慮的東西比較少,只有串聯和一個用電器,難度比較簡單。第六次的電路模擬程式在第五次的基礎上增加了並聯,而且每個用電器都有電阻了,和我之前寫的第五題的思路完全不同,需要改動的地方太多,相當於重寫,而不是迭代,難度很大。
二:設計與分析:
第四次作業:
答題判題程式-4:本題要求實現一個的答題判題程式,包括題目的輸入、試卷的輸入,答案的提交、以及答案的校驗和輸出,在之前的基礎上增加了單選題,多選題,填空題。
解題思路:設計了11個類:
Question 類
屬性:
number:題目的編號。
content:題目內容。
standardAnswer:標準答案。
obtainedScore:獲得的分數。
isDeleted:標記題目是否被刪除。
方法:
建構函式:初始化題目的基本屬性。
checkAnswer:檢查給定的答案是否正確,並返回得分。
功能:表示一個題目,包含編號、內容、標準答案,並能檢查給定的答案是否正確。
MultipleChoiceQuestion 類
繼承:繼承自Question類。
功能:處理多選題的特定邏輯,檢查答案是否完全正確或部分正確。
FillInTheBlankQuestion 類
繼承:繼承自Question類。
功能:處理填空題的特定邏輯,檢查答案是否完全正確或部分正確。
QuestionList 類
屬性:
questions:儲存題目的列表。
方法:
saveQuestion:儲存題目到列表中。
getQuestion:根據題目編號獲取題目。
功能:管理題目庫,提供儲存和獲取題目的功能。
Exam 類
屬性:
id:試卷的編號。
questionsExam:儲存考試題目的列表。
totalScore:總分。
scores:儲存每道題目的分數。
方法:
建構函式:初始化試卷的基本屬性。
addQuestion:向考試中新增題目及其分數。
judgeAnswer:判斷特定題目的答案是否正確,並返回得分。
功能:表示一次考試,包含多個題目,並能對答案進行校驗。
ExamList 類
屬性:
exams:儲存考試的列表。
方法:
saveExam:儲存考試到列表中。
getExam:根據試卷編號獲取考試。
功能:管理考試列表,提供儲存和獲取考試的功能。
Student 類
屬性:
id:學生編號。
name:學生姓名。
功能:表示學生資訊。
StudentData 類
屬性:
students:儲存學生的列表。
方法:
saveStudent:儲存學生到列表中。
getStudent:根據學生編號獲取學生。
功能:管理學生資訊庫,提供儲存和獲取學生的功能。
AnswerPaper 類
屬性:
paperId:答卷編號。
studentId:學生編號。
answers:儲存題目答案的對映。
方法:
saveAnswer:儲存題目答案。
getPaperId:獲取答卷編號。
getStudentId:獲取學生編號。
功能:表示一份答卷,包含答卷編號、學生編號和答案。
DeleteQues 類
屬性:
Deles:儲存被刪除題目的編號列表。
方法:
saveDeletedQuestion:儲存被刪除題目的編號。
功能:管理需要刪除的題目。
Main 類
方法:
main:接收命令列輸入,初始化題目庫、考試庫、學生資訊庫、刪除庫和答卷庫,讀取資料,排序資料,輸入資料,處理考試,並輸出結果。
sortDataList:根據資料型別的預定義順序對輸入資料列表進行排序。
inputData:解析單條輸入資料,並根據資料型別新增到相應的資料結構中。
processExams:處理所有答卷,計算分數,並輸出每個學生的考試結果。
extractContent:從給定的字串中提取特定模式的內容。
功能:
main 方法:程式的入口點,負責初始化資料結構,讀取輸入資料,呼叫資料處理和結果輸出的方法。
資料流分析
輸入資料:
資料來源:程式透過 Scanner 物件從命令列輸入讀取資料。
資料型別:支援多種型別的輸入資料,包括題目(#N:)、考試(#T:)、學生資訊(#X:)、答卷(#S:)和刪除題目(#D:)。
資料儲存:所有輸入資料行被儲存在 List
資料處理:
資料排序:使用 sortDataList 方法,根據資料行的字首按照預定義的順序(#N:, #K:, #Z:, #T:, #S:, #X:, #D:)進行排序,並將排序後的結果儲存在 sortedList 中。
資料解析:遍歷 sortedList,使用 inputData 方法解析每條資料行。根據資料行的字首,呼叫 extractContent 方法提取相關資訊,並將提取的資料封裝到相應的類例項中:
Question:封裝普通題目資訊。
MultipleChoiceQuestion:封裝多選題資訊。
FillInTheBlankQuestion:封裝填空題資訊。
Exam:封裝考試資訊,包括題目編號和分數。
Student:封裝學生資訊。
AnswerPaper:封裝學生答卷資訊。
DeleteQues:封裝需要刪除的題目編號。
刪除題目:
刪除操作:inputData 方法中,如果資料行以 #D: 開頭,表示需要刪除題目。提取題目編號,並在 DeleteQues 的 Deles 列表中記錄該編號。然後,在 QuestionList 中找到對應的 Question 物件,並將其 isDeleted 屬性設定為 true。
分數判斷:
總分檢查:processExams 方法遍歷 ExamList 中的所有 Exam 物件,檢查每個考試的總分是否為100分。如果不是,輸出警告資訊。
答卷比較:
答卷處理:processExams 方法遍歷 paperBank 中的所有 AnswerPaper 物件,對於每個學生的答卷:
獲取對應的 Exam 物件。
遍歷 Exam 物件中的題目列表,對於每個題目,使用 judgeAnswer 方法比較學生的答案與標準答案。
根據比較結果,計算每個題目的得分,並累加到總分中。
輸出每個學生的答題情況和總分。
輸出結果:
結果展示:對於每個學生的答卷,按照學生ID、姓名、每題得分和總分的格式輸出結果。
類圖
第五次作業:
家居強電電路模擬程式-1:模擬了一個電路系統中裝置的操作,包括開關、調速器和受控裝置(如燈具和風扇)
解題思路:設計了10個類:
Device 類(裝置基類)
屬性:
id:裝置的編號。
pin1:輸入引腳,表示輸入電壓。
pin2:輸出引腳,表示輸出電壓。
方法:
建構函式:初始化裝置的基本屬性。
功能:作為所有裝置的基類,定義了裝置的共同屬性。
ControlDevice 類(控制裝置基類)
繼承:繼承自Device類。
方法:
OutVoltage(double v):抽象方法,返回裝置的輸出電壓。
功能:作為所有控制裝置的基類,定義了控制裝置的共同行為。
ControlledDevice 類(受控裝置基類)
繼承:繼承自Device類。
方法:
setState(double v):抽象方法,根據輸入電壓設定裝置狀態。
功能:作為所有受控裝置的基類,定義了受控裝置的共同行為。
Switch 類(開關裝置)
繼承:繼承自ControlDevice類。
屬性:
state:表示開關的當前狀態。
方法:
OutVoltage(double v):返回開關的輸出電壓。
control():控制開關的狀態。
getState():返回開關的當前狀態。
功能:表示一個開關裝置,能夠控制電流的通斷。
GearSpeedRegulator 類(齒輪速度調節器)
繼承:繼承自ControlDevice類。
屬性:
flag:表示速度檔位。
方法:
OutVoltage(double v):根據速度檔位返回不同的輸出電壓。
功能:表示一個齒輪速度調節器,能夠調節輸出電壓以控制速度。
ContinuousSpeedRegulator 類(連續速度調節器)
繼承:繼承自ControlDevice類。
屬性:
parameter:表示速度引數。
方法:
OutVoltage(double v):根據速度引數返回撥整後的輸出電壓。
功能:表示一個連續速度調節器,能夠根據引數連續調節輸出電壓。
IncandescentLamp 類(白熾燈)
繼承:繼承自ControlledDevice類。
屬性:
light:表示燈的亮度。
方法:
setState(double v):根據輸入電壓設定燈的亮度。
功能:表示一個白熾燈,能夠根據電壓調整亮度。
FluorescentLamp 類(熒光燈)
繼承:繼承自ControlledDevice類。
屬性:
light:表示燈的亮度。
方法:
setState(double v):根據輸入電壓設定燈的亮度。
功能:表示一個熒光燈,能夠根據電壓調整亮度。
CeilingFan 類(吊扇)
繼承:繼承自ControlledDevice類。
屬性:
speed:表示風扇的速度。
方法:
setState(double v):根據輸入電壓設定風扇的速度。
功能:表示一個吊扇,能夠根據電壓調整轉速。
Main 類
方法:
main:程式的入口點,負責讀取輸入資料,建立裝置,設定電壓,重置電壓和輸出裝置狀態。
createDevice:根據輸入資料建立裝置例項。
SetPotential:設定裝置的電壓。
ResetPotential:重置裝置的電壓。
OutPrintf:輸出所有裝置的狀態。
功能:負責整個程式的流程控制和資料處理。
資料流分析
輸入資料:
程式透過 Scanner 物件從命令列輸入讀取資料,支援多種型別的輸入資料,包括裝置定義、控制命令和電壓設定。
資料處理:
使用 createDevice 方法根據輸入資料建立裝置例項。
使用 SetPotential 方法根據電路連線設定裝置的電壓。
使用 ResetPotential 方法在所有開關關閉時重置受控裝置的電壓。
使用 OutPrintf 方法輸出所有裝置的狀態。
類圖
第六次作業:
家居強電電路模擬程式-2:模擬了一個電路系統中裝置的操作,包括開關、調速器和受控裝置(如燈具和風扇),在之前的基礎上加了並聯電路。
解題思路:設計了12個類:
Device 類(裝置基類)
屬性:
id:裝置的編號。
pin1:輸入引腳,表示輸入電壓。
pin2:輸出引腳,表示輸出電壓。
vd:電壓差。
resistance:總電阻。
方法:
建構函式:初始化裝置的基本屬性。
getResistance:抽象方法,獲取總電阻。
功能:作為所有裝置的基類,定義了裝置的共同屬性和行為。
ControlDevice 類(控制裝置基類)
繼承:繼承自Device類。
方法:
OutVoltage(double v):抽象方法,根據輸入電壓得到輸出電壓。
getResistance:返回總電阻。
功能:作為所有控制裝置的基類,定義了控制裝置的共同行為。
ControlledDevice 類(受控裝置基類)
繼承:繼承自Device類。
方法:
setState(double v):抽象方法,根據電壓差設定狀態引數。
功能:作為所有受控裝置的基類,定義了受控裝置的共同行為。
SerialCircuit 類(串聯電路類)
屬性:
devices:儲存串聯電路中的裝置列表。
totalResistance:總電阻。
方法:
addDevice:新增裝置到串聯電路。
calculateVoltage:計算串聯電路中每個裝置的電壓差。
功能:表示一個串聯電路,管理電路中的裝置和計算電壓差。
ParallelCircuit 類(並聯電路類)
屬性:
circuits:儲存並聯電路中的串聯電路列表。
totalResistance:總電阻。
方法:
addCircuit:新增串聯電路到並聯電路。
updateResistance:更新並聯電路的總電阻。
calculateVoltage:計算並聯電路中每個串聯電路的電壓差。
功能:表示一個並聯電路,管理電路中的串聯電路和計算電壓差。
Switch 類(開關裝置)
繼承:繼承自ControlDevice類。
屬性:
state:表示開關的當前狀態。
方法:
OutVoltage(double v):返回開關的輸出電壓。
control:控制開關的狀態。
功能:表示一個開關裝置,能夠控制電流的通斷。
GearSpeedRegulator 類(齒輪速度調節器)
繼承:繼承自ControlDevice類。
屬性:
flag:表示速度檔位。
方法:
changeGear:改變速度檔位。
OutVoltage(double v):根據速度檔位返回不同的輸出電壓。
功能:表示一個齒輪速度調節器,能夠調節輸出電壓以控制速度。
ContinuousSpeedRegulator 類(連續速度調節器)
繼承:繼承自ControlDevice類。
屬性:
parameter:表示速度引數。
方法:
OutVoltage(double v):根據速度引數返回撥整後的輸出電壓。
功能:表示一個連續速度調節器,能夠根據引數連續調節輸出電壓。
IncandescentLamp 類(白熾燈)
繼承:繼承自ControlledDevice類。
屬性:
light:表示燈的亮度。
resistance:總電阻。
方法:
setState(double v):根據電壓差設定燈的亮度。
getResistance:返回總電阻。
功能:表示一個白熾燈,能夠根據電壓調整亮度。
FluorescentLamp 類(熒光燈)
繼承:繼承自ControlledDevice類。
屬性:
light:表示燈的亮度。
resistance:總電阻。
方法:
setState(double v):根據電壓差設定燈的亮度。
getResistance:返回總電阻。
功能:表示一個熒光燈,能夠根據電壓調整亮度。
CeilingFan 類(吊扇)
繼承:繼承自ControlledDevice類。
屬性:
speed:表示風扇的速度。
resistance:總電阻。
方法:
setState(double v):根據電壓差設定風扇的速度。
getResistance:返回總電阻。
功能:表示一個吊扇,能夠根據電壓調整轉速。
FloorFan 類(落地扇)
繼承:繼承自ControlledDevice類。
屬性:
speed:表示風扇的速度。
resistance:總電阻。
方法:
setState(double v):根據電壓差設定風扇的速度。
getResistance:返回總電阻。
功能:表示一個落地扇,能夠根據電壓調整轉速。
Main 類
方法:
main:程式的入口點,負責讀取輸入資料,建立裝置,設定電阻,計算電壓,列印結果。
handleLines:處理輸入的每一行資料。
setResistance:設定裝置的電阻。
processCircuit:處理電路定義。
getOrCreateDevice:獲取或建立裝置。
printResults:列印結果。
功能:
main 方法:程式的入口點,負責初始化資料結構,讀取輸入資料,呼叫資料處理和結果輸出的方法。
資料流分析
輸入資料:
程式透過 Scanner 物件從命令列輸入讀取資料,支援多種型別的輸入資料,包括裝置定義、控制命令和電壓設定。
資料處理:
使用 handleLines 方法處理輸入的每一行資料,根據資料型別建立或更新裝置狀態。
使用 setResistance 方法設定裝置的電阻。
使用 processCircuit 方法處理電路定義,建立串聯和並聯電路。
使用 getOrCreateDevice 方法獲取或建立裝置例項。
計算電壓:
使用 calculateVoltage 方法計算每個電路的電壓差。
輸出結果:
使用 printResults 方法輸出所有裝置的狀態。
類圖:
三:採坑心得:
第四次作業:
1.在處理多選題時,我認為答案只能為A-Z,所用正規表示式去掉那些不符合這個格式的資料,導致錯了前面的三個測試點
錯誤的正規表示式如下
點選檢視程式碼
String regex6 = "#Z:\\d\\s#Q:.+\\s#A:[\\sA-Z]*";
點選檢視程式碼
String regex6 = "#Z:.+#Q:.+#A:.+";
點選檢視程式碼
givenAnswerSet1.retainAll(correctAnswerSet1); // 保留交集
if (givenAnswerSet1.size() == correctAnswerSet1.size() && givenAnswerSet1.size() == givenAnswers1.length) {
return 1; // 完全正確
}
else if (givenAnswerSet1.size() > 0 && givenAnswerSet1.size() == givenAnswers1.length) {
return 2; // 不包含錯誤答案且部分正確
}
else{
return 0; // 錯誤
}
3.沒有正確處理末尾的空格,最後一個測試點一直為格式錯誤,在問其他同學後得知,最後一個測試點為若答案末尾有兩個空格,只能去掉最後一個空格,但是在判斷答案時去掉所有空格判斷
程式碼如下:
點選檢視程式碼
if(answer.charAt(answer.length()-1) == ' ') //去掉末尾的一個空格
answer = answer.substring(0, answer.length()-1);
第五次作業:
1.在遍歷電路設定電位時,我一開始我只考慮到在用電器前面的開關,如果為開啟,電位就設定為0,但是沒有想到在用電器後面的開關,會影響前面的用電器的電位導致有幾個測試點沒過。
所以我在後面遍歷所有開關,若有開啟的則把所有的電位置0。
程式碼如下:
點選檢視程式碼
public static void ResetPotential(List<Switch> switches, List<IncandescentLamp> whites,
List<FluorescentLamp> suns, List<CeilingFan> fans) //重置電位
{
int flat = 0;
for(Switch a : switches){
if(!a.state){
flat = 1; //如果存在開啟的開關
break;
}
}
if(flat == 1){ //把所有用電器的輸入電位置0
for(IncandescentLamp a : whites){
a.pin1 = 0.0;
}
for(FluorescentLamp a : suns)
{
a.pin1 = 0.0;
}
for(CeilingFan a : fans){
a.pin1 = 0.0;
}
}
}
第六次作業:
這次作業雖然只是在上次的作業的基礎上加入並聯,但是感覺難度提升很大。我之前的思路是透過遍歷電路,給每個裝置的輸入和輸出引腳的電位賦值。
在經過許多思考和嘗試後,認為這種方法在這題上面行不通,最後我是透過計算每個裝置的總電阻,然後透過分壓來直接計算每個裝置的電壓差。
1.一開始我的思路為先遍歷所有的串聯電路,然後再處理並聯電路。這樣會導致在處理總串聯電路總算電阻時,將還沒有計算的並聯電路的電阻當成0,
加入計算到總電阻中導致了錯誤。
改進方法:後面我改成了先遍歷子串聯電路,判斷每個子串聯電路的狀態,然後再計算總電阻,再遍歷並聯和總串聯電路。
2.在根據子串聯電路的狀態來判斷並聯電路的狀態時出錯,然後導致判斷總電路的狀態錯誤,沒有考慮清楚短路和斷路的問題。
改進方法:先透過子串聯電路的狀態來設定並聯電路的狀態,最後判斷總電路的狀態。判斷並聯電路的狀態時比較複雜,若子串聯的狀態全為斷路則並聯電路為斷路,若存在子串聯的狀態有一個為短路,則並聯電路為短路,其他為通路。
程式碼如下:
點選檢視程式碼
//判斷並聯電路的狀態
boolean allZero = true; //全零為斷路
boolean hasNegativeOne = false; //存在一條路為短路
for(SerialCircuit cir : parallelCircuit.circuits){
int flat = cir.flat;
if (flat != 0) {
allZero = false;
}
if (flat == -1) {
hasNegativeOne = true;
}
parallelCircuit.updateResistance();
}
if (allZero) {
parallelCircuit.flat = 0;
parallelCircuit.totalResistance = 0;
} else if (hasNegativeOne) {
parallelCircuit.flat = -1;
parallelCircuit.totalResistance = 0;
} else {
parallelCircuit.flat = 1;
}
四:改進建議:
第四次作業:
關鍵指標分析
語句數 (Statements): 247條語句,這個數量表明程式碼檔案中包含了相當多的操作。
分支語句百分比 (Percent Branch Statements): 21.5%,這個比例表明程式碼中包含了較多的條件判斷,這可能增加程式碼的複雜性。
方法呼叫語句 (Method Call Statements): 147,這表明程式碼中方法呼叫較為頻繁,這通常是好的,但如果方法呼叫鏈過長,可能會影響效能。
註釋率 (Percent Lines with Comments): 6.8%,註釋率較低,這可能影響程式碼的可讀性和可維護性。
類和介面數量 (Classes and Interfaces): 11,這個數量適中,但需要確保每個類和介面都有清晰的職責。
改進建議
重構程式碼: 考慮將Main.java檔案中的程式碼重構為更小的模組,每個模組負責一個單一職責,以降低複雜度。
增加註釋: 為程式碼新增更多的註釋,特別是對於複雜的邏輯和方法,以提高程式碼的可讀性。
簡化複雜方法: 嘗試簡化最複雜的方法,可能透過提取子方法或重構邏輯來實現。
第五次作業:
關鍵指標分析
語句數 (Statements): 264條語句,這個數量表明程式碼檔案中包含了相當多的操作。
分支語句百分比 (Percent Branch Statements): 33.7%,這個比例表明程式碼中包含了較多的條件判斷,這可能增加程式碼的複雜性。
方法呼叫語句 (Method Call Statements): 126,這表明程式碼中方法呼叫較為頻繁,這通常是好的,但如果方法呼叫鏈過長,可能會影響效能。
註釋率 (Percent Lines with Comments): 2.0%,這個比例非常低,建議增加註釋以提高程式碼的可讀性和可維護性。
類和介面數量 (Classes and Interfaces): 8,這個數量適中,但需要確保每個類和介面都有清晰的職責。
每類方法數 (Methods per Class): 2.88,這個數字表明每個類中的方法數量不多,這有助於保持類的職責單一。
每方法平均語句數 (Average Statements per Method): 11.04,這個數字表明方法的平均長度,建議保持方法簡短和專注。
最複雜方法行數 (Line Number of Most Complex Method): 218行,這表明至少有一個方法非常複雜,可能需要重構。
最複雜方法名稱 (Name of Most Complex Method): Main.SetPotential(),這表明該方法可能是重構的候選。
最大複雜度 (Maximum Complexity): 45,這個複雜度相對較高,建議簡化邏輯。
最深塊行數 (Line Number of Deepest Block): 292,這可能意味著有深層巢狀的控制結構,這可能影響程式碼的可讀性。
平均塊深度 (Average Block Depth): 3.42,這個深度相對較低,表明程式碼的巢狀結構不深。
平均複雜度 (Average Complexity): 5.23,這個複雜度表明方法的平均複雜性,建議簡化方法邏輯。
改進建議
重構複雜方法: 特別是Main.SetPotential(),考慮將其拆分為多個小方法。
增加註釋: 提高程式碼的註釋率,使其更易於理解和維護。
簡化邏輯: 嘗試簡化複雜的條件邏輯,減少巢狀的深度。
使用設計模式: 考慮應用設計模式,如工廠模式來建立物件,或策略模式來封裝演算法的變化。
最佳化類結構: 確保每個類都有單一職責,避免類承擔過多的功能。
第六次作業:
關鍵指標分析
圈複雜度(Maximum Complexity): 9 是一個較高的複雜度,表明 Main.main() 方法可能執行了過多的任務,違反了單一職責原則。
最複雜方法的行數(Line Number of Most Complex Method): 200 行,這表明該方法可能過於複雜,難以理解和測試。
方法呼叫語句(Method Call Statements): 134 表明程式碼中方法呼叫較為頻繁,這可能是程式碼重用性高的一個好跡象,但也可能意味著方法之間的耦合度高。
註釋率(Percent Lines with Comments): 6.5%,註釋率較低,這可能影響程式碼的可讀性和可維護性。
平均塊深度(Average Block Depth): 1.71,這個值相對較低,表明程式碼的巢狀結構不深,這通常是好的。
類和介面數量(Classes and Interfaces): 12,這個數量適中,但需要確保每個類和介面都有清晰的職責。
改進建議
重構 Main.main() 方法:考慮將 Main.main() 方法拆分成多個小方法,每個方法負責一個單一職責,以降低複雜度。
增加註釋:為程式碼新增更多的註釋,特別是對於複雜的邏輯和方法,以提高程式碼的可讀性。
簡化複雜方法:嘗試簡化最複雜方法 Main.main(),可能透過提取子方法或重構邏輯來實現。
五:總結:
本階段的三次題目集涵蓋了Java程式設計的多個方面,包括物件導向程式設計、資料結構、異常處理、正規表示式、輸入輸出處理等。
透過這三次的題目,我學會了Java集合框架,瞭解它們的用途和效能特點,對字串的處理,split,trim,substring,indexof等方法,學會了物件導向的思想,
接下來應該深入學習異常處理:學習如何自定義異常,並在程式碼中合理使用try-catch-finally塊來處理異常,以及一些java模型,如裝飾模型,橋接模型等,在寫程式碼時可以啟到事半功倍的效果,便於迭代。
學習心得:在寫題時不能看個大概就直接開始寫,應該在反覆閱讀題目設計好解題思路,畫出簡易的類圖後開始寫。
不然在後面除錯程式碼改錯時會花費大量時間
建議及意見:希望老師在上課時多講一些後面大作業可能需要用到的語法和技巧,同時建議能夠給出大作業中所有的測試點,有的時候一個測試點需要猜幾個小時。