昨天 Day 24 幫每一筆消費加入了日期,現在記帳紀錄已經可以顯示:
加入日期之後,今天就可以真正利用日期來做新的功能:
輸入指定日期,查詢當天所有消費,並計算當日總支出。
例如輸入:
請輸入查詢日期:2026-10-02
程式自動找出:
===== 2026-10-02 消費紀錄 =====
午餐|150 元|飲食
電影|300 元|娛樂
當日共有 2 筆消費
當日總支出:450 元
今天要加入「依日期查詢」
昨天的主選單:
===== 個人記帳系統 =====
今天加入:
所以新的選單:
===== 個人記帳系統 =====
第一步:讓使用者輸入日期
建立新的 Method:
public static void searchByDate()
先讓使用者輸入:
System.out.print(
"請輸入查詢日期(YYYY-MM-DD):"
);
String input =
scanner.nextLine();
例如:
2026-10-02
不過現在的 input 是:
String
而 Expense 裡面的日期是:
LocalDate
所以還不能直接比較。
第二步:把 String 轉成 LocalDate
昨天學過:
LocalDate.parse()
所以可以:
LocalDate searchDate =
LocalDate.parse(input);
這樣:
"2026-10-02"
↓
LocalDate.parse()
↓
2026-10-02
就得到一個真正的 LocalDate。
第三步:找出符合日期的 Expense
現在需要檢查 ArrayList 裡的每一筆資料。
使用之前學過的 for:
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
}
接著比較:
if (
expense.getDate()
.equals(searchDate)
)
如果日期相同,就代表這筆消費是使用者要找的。
為什麼使用 equals()?
如果是數字:
if (amount == 100)
可以使用:
==
但是今天比較的是 LocalDate Object。
所以使用:
equals()
例如:
expense.getDate().equals(searchDate)
可以簡單理解成:
這筆消費日期
↓
2026-10-02
↓
和查詢日期一樣嗎?
↓
2026-10-02
↓
是 ✓
第四步:顯示符合的紀錄
如果日期相同:
System.out.println(
expense.getItem()
+ "|"
+ expense.getAmount()
+ " 元|"
+ expense.getCategory()
);
假設找到:
午餐|150 元|飲食
電影|300 元|娛樂
就會全部顯示出來。
第五步:順便計算當日總支出
建立:
int total = 0;
每找到一筆:
total += expense.getAmount();
例如:
午餐 150
↓
total = 150
電影 300
↓
total = 450
最後:
當日總支出:450 元
第六步:計算有幾筆消費
另外建立:
int count = 0;
每找到一筆:
count++;
例如找到:
午餐
電影
最後:
count = 2
所以可以顯示:
當日共有 2 筆消費
如果完全找不到呢?
假設輸入:
2026-09-20
但是沒有任何紀錄。
這時:
count
最後仍然是:
0
所以可以:
if (count == 0) {
System.out.println(
"這一天沒有消費紀錄。"
);
} else {
System.out.println(
"當日共有 "
+ count
+ " 筆消費"
);
System.out.println(
"當日總支出:"
+ total
+ " 元"
);
}
完整 searchByDate()
今天最重要的新 Method:
public static void searchByDate() {
System.out.println();
System.out.println(
"===== 依日期查詢 ====="
);
System.out.print(
"請輸入查詢日期(YYYY-MM-DD):"
);
String input =
scanner.nextLine();
try {
LocalDate searchDate =
LocalDate.parse(input);
int total = 0;
int count = 0;
System.out.println();
System.out.println(
"===== "
+ searchDate
+ " 消費紀錄 ====="
);
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
if (
expense.getDate()
.equals(searchDate)
) {
System.out.println(
expense.getItem()
+ "|"
+ expense.getAmount()
+ " 元|"
+ expense.getCategory()
);
total +=
expense.getAmount();
count++;
}
}
if (count == 0) {
System.out.println(
"這一天沒有消費紀錄。"
);
} else {
System.out.println();
System.out.println(
"當日共有 "
+ count
+ " 筆消費"
);
System.out.println(
"當日總支出:"
+ total
+ " 元"
);
}
} catch (Exception e) {
System.out.println(
"日期格式錯誤,請使用 YYYY-MM-DD。"
);
}
}
今天又用到了 Exception Handling
如果使用者輸入:
10/02
或:
2026/10/02
這些都不是:
YYYY-MM-DD
所以:
LocalDate.parse(input);
可能發生錯誤。
因此使用:
try {
// 轉換日期
} catch (Exception e) {
// 處理錯誤
}
如果輸入錯誤:
請輸入查詢日期(YYYY-MM-DD):2026/10/02
程式顯示:
日期格式錯誤,請使用 YYYY-MM-DD。
而不是整個程式直接停止。
Day 25 完整程式
今天把依日期查詢整合進目前的版本:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDate;
class Expense {
private LocalDate date;
private String item;
private int amount;
private String category;
Expense(
LocalDate date,
String item,
int amount,
String category
) {
this.date = date;
this.item = item;
this.amount = amount;
this.category = category;
}
public LocalDate getDate() {
return date;
}
public String getItem() {
return item;
}
public int getAmount() {
return amount;
}
public String getCategory() {
return category;
}
public void setItem(String item) {
this.item = item;
}
public void setAmount(int amount) {
if (amount >= 0) {
this.amount = amount;
}
}
public void setCategory(String category) {
this.category = category;
}
}
public class Main {
static Scanner scanner =
new Scanner(System.in);
static ArrayList expenses =
new ArrayList<>();
static int budget = 0;
public static void main(String[] args) {
loadExpenses();
boolean running = true;
while (running) {
showMenu();
try {
System.out.print("請選擇功能:");
int choice =
scanner.nextInt();
scanner.nextLine();
switch (choice) {
case 1:
addExpense();
break;
case 2:
showExpenses();
break;
case 3:
calculateTotal();
break;
case 4:
categoryStatistics();
break;
case 5:
deleteExpense();
break;
case 6:
editExpense();
break;
case 7:
setBudget();
break;
case 8:
checkBudget();
break;
case 9:
searchByDate();
break;
case 10:
running = false;
System.out.println(
"已離開個人記帳系統!"
);
break;
default:
System.out.println(
"沒有這個選項!"
);
}
} catch (InputMismatchException e) {
System.out.println(
"輸入錯誤,請輸入數字!"
);
scanner.nextLine();
}
}
scanner.close();
}
public static void showMenu() {
System.out.println();
System.out.println(
"===== 個人記帳系統 ====="
);
System.out.println("1. 新增消費");
System.out.println("2. 查看紀錄");
System.out.println("3. 查看總支出");
System.out.println("4. 分類支出統計");
System.out.println("5. 刪除消費紀錄");
System.out.println("6. 修改消費紀錄");
System.out.println("7. 設定預算");
System.out.println("8. 查看預算狀況");
System.out.println("9. 依日期查詢");
System.out.println("10. 離開");
}
public static void addExpense() {
System.out.println();
System.out.println(
"===== 新增消費 ====="
);
LocalDate today =
LocalDate.now();
System.out.print(
"請輸入消費項目:"
);
String item =
scanner.nextLine();
System.out.print(
"請輸入消費金額:"
);
int amount =
scanner.nextInt();
String category =
chooseCategory();
Expense expense =
new Expense(
today,
item,
amount,
category
);
expenses.add(expense);
saveExpenses();
System.out.println(
"記帳成功!"
);
System.out.println(
"日期:" + today
);
}
public static String chooseCategory() {
System.out.println();
System.out.println(
"請選擇消費分類:"
);
System.out.println("1. 飲食");
System.out.println("2. 交通");
System.out.println("3. 購物");
System.out.println("4. 娛樂");
System.out.print(
"請輸入選項:"
);
int choice =
scanner.nextInt();
scanner.nextLine();
switch (choice) {
case 1:
return "飲食";
case 2:
return "交通";
case 3:
return "購物";
case 4:
return "娛樂";
default:
return "其他";
}
}
public static void showExpenses() {
System.out.println();
System.out.println(
"===== 消費紀錄 ====="
);
if (expenses.size() == 0) {
System.out.println(
"目前沒有消費紀錄。"
);
return;
}
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
System.out.println(
(i + 1) + ". "
+ expense.getDate()
+ "|"
+ expense.getItem()
+ "|"
+ expense.getAmount()
+ " 元|"
+ expense.getCategory()
);
}
}
public static void calculateTotal() {
int total = 0;
for (int i = 0;
i < expenses.size();
i++) {
total +=
expenses.get(i).getAmount();
}
System.out.println();
System.out.println(
"===== 支出統計 ====="
);
System.out.println(
"目前共有 "
+ expenses.size()
+ " 筆消費"
);
System.out.println(
"總支出:"
+ total
+ " 元"
);
}
public static void categoryStatistics() {
int foodTotal = 0;
int transportTotal = 0;
int shoppingTotal = 0;
int entertainmentTotal = 0;
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
switch (expense.getCategory()) {
case "飲食":
foodTotal +=
expense.getAmount();
break;
case "交通":
transportTotal +=
expense.getAmount();
break;
case "購物":
shoppingTotal +=
expense.getAmount();
break;
case "娛樂":
entertainmentTotal +=
expense.getAmount();
break;
}
}
System.out.println();
System.out.println(
"===== 分類支出統計 ====="
);
System.out.println(
"飲食:" + foodTotal + " 元"
);
System.out.println(
"交通:" + transportTotal + " 元"
);
System.out.println(
"購物:" + shoppingTotal + " 元"
);
System.out.println(
"娛樂:"
+ entertainmentTotal
+ " 元"
);
}
public static void deleteExpense() {
System.out.println();
System.out.println(
"===== 刪除消費紀錄 ====="
);
if (expenses.size() == 0) {
System.out.println(
"目前沒有可以刪除的紀錄。"
);
return;
}
showExpenses();
System.out.print(
"請輸入要刪除的紀錄編號:"
);
int number =
scanner.nextInt();
scanner.nextLine();
if (
number >= 1
&& number <= expenses.size()
) {
Expense deleted =
expenses.remove(number - 1);
saveExpenses();
System.out.println(
"已刪除:"
+ deleted.getItem()
);
} else {
System.out.println(
"沒有這筆紀錄!"
);
}
}
public static void editExpense() {
System.out.println();
System.out.println(
"===== 修改消費紀錄 ====="
);
if (expenses.size() == 0) {
System.out.println(
"目前沒有可以修改的紀錄。"
);
return;
}
showExpenses();
System.out.print(
"請輸入要修改的紀錄編號:"
);
int number =
scanner.nextInt();
scanner.nextLine();
if (
number >= 1
&& number <= expenses.size()
) {
Expense expense =
expenses.get(number - 1);
System.out.print(
"請輸入新的消費項目:"
);
String item =
scanner.nextLine();
System.out.print(
"請輸入新的消費金額:"
);
int amount =
scanner.nextInt();
String category =
chooseCategory();
expense.setItem(item);
expense.setAmount(amount);
expense.setCategory(category);
saveExpenses();
System.out.println(
"修改成功!"
);
} else {
System.out.println(
"沒有這筆紀錄!"
);
}
}
public static void setBudget() {
System.out.println();
System.out.println(
"===== 設定預算 ====="
);
System.out.print(
"請輸入預算金額:"
);
budget =
scanner.nextInt();
scanner.nextLine();
System.out.println(
"預算設定成功:"
+ budget
+ " 元"
);
}
public static void checkBudget() {
System.out.println();
System.out.println(
"===== 預算狀況 ====="
);
if (budget <= 0) {
System.out.println(
"尚未設定預算。"
);
return;
}
int total = 0;
for (int i = 0;
i < expenses.size();
i++) {
total +=
expenses.get(i).getAmount();
}
System.out.println(
"目前預算:"
+ budget
+ " 元"
);
System.out.println(
"目前支出:"
+ total
+ " 元"
);
if (total > budget) {
int over =
total - budget;
System.out.println(
"已超出預算 "
+ over
+ " 元!"
);
} else {
int remaining =
budget - total;
System.out.println(
"剩餘預算:"
+ remaining
+ " 元"
);
}
}
public static void searchByDate() {
System.out.println();
System.out.println(
"===== 依日期查詢 ====="
);
System.out.print(
"請輸入查詢日期(YYYY-MM-DD):"
);
String input =
scanner.nextLine();
try {
LocalDate searchDate =
LocalDate.parse(input);
int total = 0;
int count = 0;
System.out.println();
System.out.println(
"===== "
+ searchDate
+ " 消費紀錄 ====="
);
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
if (
expense.getDate()
.equals(searchDate)
) {
System.out.println(
expense.getItem()
+ "|"
+ expense.getAmount()
+ " 元|"
+ expense.getCategory()
);
total +=
expense.getAmount();
count++;
}
}
if (count == 0) {
System.out.println(
"這一天沒有消費紀錄。"
);
} else {
System.out.println();
System.out.println(
"當日共有 "
+ count
+ " 筆消費"
);
System.out.println(
"當日總支出:"
+ total
+ " 元"
);
}
} catch (Exception e) {
System.out.println(
"日期格式錯誤,請使用 YYYY-MM-DD。"
);
}
}
public static void saveExpenses() {
try {
FileWriter writer =
new FileWriter(
"expenses.txt"
);
for (int i = 0;
i < expenses.size();
i++) {
Expense expense =
expenses.get(i);
writer.write(
expense.getDate()
+ ","
+ expense.getItem()
+ ","
+ expense.getAmount()
+ ","
+ expense.getCategory()
+ "\n"
);
}
writer.close();
} catch (IOException e) {
System.out.println(
"檔案儲存失敗!"
);
}
}
public static void loadExpenses() {
try {
File file =
new File("expenses.txt");
Scanner fileScanner =
new Scanner(file);
while (
fileScanner.hasNextLine()
) {
String line =
fileScanner.nextLine();
String[] data =
line.split(",");
if (data.length == 4) {
LocalDate date =
LocalDate.parse(
data[0]
);
String item =
data[1];
int amount =
Integer.parseInt(
data[2]
);
String category =
data[3];
Expense expense =
new Expense(
date,
item,
amount,
category
);
expenses.add(expense);
}
}
fileScanner.close();
System.out.println(
"記帳資料讀取成功!"
);
} catch (Exception e) {
System.out.println(
"目前沒有已儲存的記帳資料。"
);
}
}
}
實際測試
假設目前有:
===== 消費紀錄 =====
選擇:
請選擇功能:9
輸入:
===== 依日期查詢 =====
請輸入查詢日期(YYYY-MM-DD):2026-10-02
結果:
===== 2026-10-02 消費紀錄 =====
午餐|150 元|飲食
電影|300 元|娛樂
飲料|50 元|飲食
當日共有 3 筆消費
當日總支出:500 元
這樣就不需要自己從所有紀錄中慢慢找。
如果查詢沒有紀錄的日期
輸入:
請輸入查詢日期(YYYY-MM-DD):2026-09-30
結果:
===== 2026-09-30 消費紀錄 =====
這一天沒有消費紀錄。
如果日期輸入錯誤
例如:
請輸入查詢日期(YYYY-MM-DD):10/02
程式不會直接當掉,而是:
日期格式錯誤,請使用 YYYY-MM-DD。
這也是之前學過的 Exception Handling 再次派上用場。
今天學到的重點
今天沒有加入很多新的 Java 語法,而是把昨天的 LocalDate 真正應用在查詢功能。
最重要的是:
LocalDate searchDate =
LocalDate.parse(input);
把使用者輸入的文字轉成日期。
再利用:
expense.getDate().equals(searchDate)
判斷兩個日期是否相同。
同時利用:
total += expense.getAmount();
count++;
在搜尋資料的過程中順便完成統計。
所以今天其實把:
LocalDate
+
ArrayList
+
for
+
if
+
equals()
+
Method
+
Exception Handling
全部組合在同一個功能裡。
Day 25 學習心得
今天完成了依日期查詢消費紀錄的功能。昨天加入日期時,主要只是讓每一筆 Expense 多保存一個資訊;今天開始真正利用日期進行資料搜尋。
透過 LocalDate.parse() 可以把使用者輸入的文字轉換成日期,再使用 equals() 比較每筆消費的日期。找到符合的資料後,我也利用迴圈同時計算當天的消費筆數和總支出。
我覺得做到這裡,記帳系統已經不只是單純把資料存起來,而是開始可以按照條件找出需要的資訊。這也讓我比較理解為什麼前面要替 Expense 加入日期,因為資料越完整,後面可以實作的功能也越多。