不知不覺已經來到 Day 29。
前面幾天一直在增加新的功能,現在我的 Java 個人記帳系統已經可以做到:
新增消費
查看消費紀錄
修改消費紀錄
刪除消費紀錄
計算總支出
分類支出統計
設定與檢查預算
依日期查詢
關鍵字搜尋
最高與最低支出分析
月支出統計
檔案儲存與讀取
自動儲存
功能越來越多之後,我發現「程式可以執行」並不代表「程式真的沒有問題」。
所以今天不繼續增加新功能,而是要進行一個完整程式很重要的階段:
Testing(測試)與 Debugging(除錯)。
什麼是 Testing?
Testing 就是:
實際操作程式,確認每個功能是不是按照預期執行。
以前寫比較小的程式時,可能只要執行一次:
程式可以跑
→ 完成
但是現在的記帳系統有十幾個功能,所以需要一個一個測試。
今天我先列出測試項目:
測試 1:新增消費
測試 2:查看紀錄
測試 3:修改紀錄
測試 4:刪除紀錄
測試 5:總支出
測試 6:分類統計
測試 7:預算功能
測試 8:日期查詢
測試 9:關鍵字搜尋
測試 10:最高/最低支出
測試 11:月支出統計
測試 12:檔案儲存
測試 13:重新開啟程式
測試 14:錯誤輸入
這次不是只測「正常情況」,也要故意輸入一些錯誤資料。
測試一:新增消費
先輸入:
請輸入消費項目:早餐
請輸入消費金額:60
請選擇消費分類:1
結果:
記帳成功!
日期:2026-10-02
查看紀錄:
2026-10-02|早餐|60 元|飲食
這部分正常。
測試二:新增後有沒有自動儲存?
打開:
expenses.txt
看到:
2026-10-02,早餐,60,飲食
代表 Day 23 加入的:
saveExpenses();
有正常執行。
測試三:重新啟動程式
這次直接關閉程式,再重新執行。
畫面:
記帳資料讀取成功!
選擇:
結果:
代表:
loadExpenses();
也有正常運作。
現在資料流程已經是:
新增資料
↓
ArrayList
↓
saveExpenses()
↓
expenses.txt
↓
關閉程式
↓
重新開啟
↓
loadExpenses()
↓
ArrayList
測試四:刪除不存在的紀錄
假設目前只有:
卻輸入:
請輸入要刪除的紀錄編號:10
程式會經過:
if (
number >= 1
&& number <= expenses.size()
)
因為:
10 <= 2
是 false。
所以顯示:
沒有這筆紀錄!
程式不會當掉。
測試五:沒有資料時執行功能
如果:
expenses.size() == 0
這時候使用:
刪除
修改
最高最低分析
都可能發生問題。
所以之前已經加入:
if (expenses.size() == 0) {
System.out.println(
"目前沒有消費紀錄。"
);
return;
}
這個 return 可以直接結束 Method。
這次測試後,我更理解為什麼之前一直要做「空資料檢查」。
測試六:月份輸入錯誤
月支出統計正常應該輸入:
1 ~ 12
故意輸入:
15
因為 Day 28 已經加入:
if (month < 1 || month > 12) {
System.out.println(
"月份必須是 1~12。"
);
return;
}
所以:
月份必須是 1~12。
測試成功。
測試七:日期格式輸入錯誤
正常:
2026-10-02
故意輸入:
2026/10/02
LocalDate.parse() 無法正常轉換。
但因為有:
try {
LocalDate searchDate =
LocalDate.parse(input);
} catch (Exception e) {
System.out.println(
"日期格式錯誤,請使用 YYYY-MM-DD。"
);
}
所以程式顯示:
日期格式錯誤,請使用 YYYY-MM-DD。
而不是直接停止。
測試八:搜尋不存在的關鍵字
輸入:
請輸入搜尋關鍵字:飛機
如果沒有資料:
找不到符合的消費紀錄。
這個功能也正常。
測試九:預算功能發現一個問題
今天測試時,我發現一個之前沒有處理好的地方。
假設:
設定預算:5000 元
程式執行期間可以正常看到:
目前預算:5000 元
目前支出:1200 元
剩餘預算:3800 元
但是關閉程式再重新開啟後:
static int budget = 0;
預算又變回:
0
原因是目前只有:
消費紀錄
有存進檔案。
預算沒有儲存。
這就是今天 Testing 發現的 Bug。
修正:把預算也存進檔案
今天新增:
budget.txt
專門保存預算。
需要使用的還是之前學過的:
File
FileWriter
Scanner
建立 saveBudget()
public static void saveBudget() {
try {
FileWriter writer =
new FileWriter(
"budget.txt"
);
writer.write(
String.valueOf(budget)
);
writer.close();
} catch (IOException e) {
System.out.println(
"預算儲存失敗!"
);
}
}
假設:
budget = 5000;
budget.txt 就會變成:
5000
String.valueOf() 是什麼?
writer.write() 比較適合寫入文字。
但是:
budget
是:
int
所以使用:
String.valueOf(budget)
把:
int 5000
轉成:
String "5000"
再寫進檔案。
設定預算後自動儲存
原本:
budget =
scanner.nextInt();
今天加:
saveBudget();
所以:
budget =
scanner.nextInt();
scanner.nextLine();
saveBudget();
現在設定預算後,也會自動儲存。
再建立 loadBudget()
重新開啟程式時,需要讀回預算:
public static void loadBudget() {
try {
File file =
new File(
"budget.txt"
);
Scanner fileScanner =
new Scanner(file);
if (fileScanner.hasNextLine()) {
budget =
Integer.parseInt(
fileScanner.nextLine()
);
}
fileScanner.close();
} catch (Exception e) {
budget = 0;
}
}
如果:
budget.txt
裡面是:
5000
透過:
Integer.parseInt()
轉回:
int 5000
最後:
budget = 5000;
啟動時讀取兩種資料
原本:
loadExpenses();
現在改成:
loadExpenses();
loadBudget();
所以:
啟動程式
↓
loadExpenses()
↓
讀取消費紀錄
↓
loadBudget()
↓
讀取預算
↓
開始操作
這樣重新啟動程式後,預算也不會消失了。
今天最後修正 setBudget()
public static void setBudget() {
System.out.println();
System.out.println(
"===== 設定預算 ====="
);
System.out.print(
"請輸入預算金額:"
);
budget =
scanner.nextInt();
scanner.nextLine();
if (budget < 0) {
System.out.println(
"預算不能小於 0!"
);
budget = 0;
return;
}
saveBudget();
System.out.println(
"預算設定成功:"
+ budget
+ " 元"
);
}
這次也順便避免:
預算 = -5000
這種不合理的輸入。
Day 29 最終新增的程式
今天不需要把前面幾百行完全重寫,主要是在 Day 28 完整版本中加入三個地方。
第一個是在 main():
public static void main(String[] args) {
loadExpenses();
loadBudget();
boolean running = true;
// 原本程式繼續...
}
第二個是修改 setBudget():
public static void setBudget() {
System.out.println();
System.out.println(
"===== 設定預算 ====="
);
try {
System.out.print(
"請輸入預算金額:"
);
int newBudget =
scanner.nextInt();
scanner.nextLine();
if (newBudget < 0) {
System.out.println(
"預算不能小於 0!"
);
return;
}
budget = newBudget;
saveBudget();
System.out.println(
"預算設定成功:"
+ budget
+ " 元"
);
} catch (InputMismatchException e) {
System.out.println(
"預算必須輸入數字!"
);
scanner.nextLine();
}
}
這裡我又做了一個小修正。
不是直接:
budget = scanner.nextInt();
而是先:
int newBudget =
scanner.nextInt();
確認它不是負數之後:
budget = newBudget;
這樣錯誤輸入就不會把原本的預算改掉。
第三個是加入:
public static void saveBudget() {
try {
FileWriter writer =
new FileWriter(
"budget.txt"
);
writer.write(
String.valueOf(budget)
);
writer.close();
} catch (IOException e) {
System.out.println(
"預算儲存失敗!"
);
}
}
public static void loadBudget() {
try {
File file =
new File(
"budget.txt"
);
Scanner fileScanner =
new Scanner(file);
if (fileScanner.hasNextLine()) {
budget =
Integer.parseInt(
fileScanner.nextLine()
);
}
fileScanner.close();
} catch (Exception e) {
budget = 0;
}
}
其他功能則沿用 Day 28 的版本。
最後做一次完整測試
我整理了一次目前系統的測試結果:
測試項目 測試結果
新增消費 ✓ 正常
查看紀錄 ✓ 正常
修改紀錄 ✓ 正常
刪除紀錄 ✓ 正常
總支出統計 ✓ 正常
分類統計 ✓ 正常
預算設定 ✓ 正常
預算重新啟動後保留 ✓ 修正完成
日期查詢 ✓ 正常
錯誤日期格式 ✓ 有提示
關鍵字搜尋 ✓ 正常
最高/最低支出 ✓ 正常
月支出統計 ✓ 正常
錯誤月份 ✓ 有提示
消費資料自動儲存 ✓ 正常
關閉後重新讀取 ✓ 正常
今天最大的收穫反而不是寫出一個新的功能,而是發現:
測試本身就是程式開發的一部分。
Testing 和 Debugging
今天也終於比較理解這兩個常看到的英文。
Testing
= 測試程式
= 找出問題
而:
Debugging
= 除錯
= 找到問題後修正
例如今天:
重新開啟程式
↓
發現預算消失
↓
Testing 找到問題
↓
檢查原因
↓
預算沒有寫進檔案
↓
加入 saveBudget()
加入 loadBudget()
↓
Debugging 完成
↓
再次測試
這就是一個很簡單的軟體測試與除錯流程。
Day 29 學習心得
今天沒有繼續增加很多新功能,而是把目前完成的記帳系統重新操作一次,測試新增、修改、刪除、查詢、統計、預算和檔案儲存等功能。
在測試過程中,我發現原本的預算只存在程式執行期間,只要重新啟動程式就會變回 0。因此今天另外使用 budget.txt 保存預算,並加入 saveBudget() 和 loadBudget(),讓預算也能像消費紀錄一樣保存下來。
這次讓我發現,程式能夠成功執行並不代表已經完全沒有問題。有些 Bug 必須真的操作不同情況才會發現。Testing 是找出問題,而 Debugging 則是找出原因並修正問題。
從 Day 1 只有幾行 System.out.println(),到現在已經有一套可以新增、修改、刪除、儲存、搜尋和分析消費資料的記帳系統。剩下最後一天,我想把這 30 天完成的內容整理起來,看看自己到底學會了哪些 Java 技術。