昨天完成了「預算與超支提醒」功能,目前記帳系統已經可以新增、查看、修改、刪除、統計消費,也可以設定預算。
不過目前還有一個很大的問題。
假設今天輸入:
只要把 Java 程式關掉,再重新執行:
===== 消費紀錄 =====
目前沒有消費紀錄。
全部都消失了。
原因是目前資料只存在 ArrayList 裡,而 ArrayList 是程式執行期間暫時存在的資料。
所以 Day 21 要開始學習 File 檔案處理,把記帳紀錄真正儲存到電腦裡。
為什麼關掉程式資料會消失?
目前我們使用:
static ArrayList expenses = new ArrayList<>();
當程式執行時:
Java 程式
↓
ArrayList
↓
早餐、午餐、捷運……
但是當程式結束:
Java 程式關閉
↓
ArrayList 消失
↓
資料也跟著消失
所以需要把資料寫進真正的檔案,例如:
expenses.txt
這樣即使 Java 程式關閉,檔案仍然存在。
今天先認識 FileWriter
Java 可以使用:
FileWriter
把文字寫入檔案。
首先需要:
import java.io.FileWriter;
另外,檔案操作有可能發生錯誤,所以還需要:
import java.io.IOException;
今天會用到:
try
和:
catch
這剛好也是 Day 15 學過的 Exception Handling。
第一個檔案寫入程式
先從最簡單的開始:
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
FileWriter writer =
new FileWriter("expenses.txt");
writer.write("早餐,60,飲食");
writer.close();
System.out.println("檔案儲存成功!");
} catch (IOException e) {
System.out.println("檔案儲存失敗!");
}
}
}
執行之後,就會產生:
expenses.txt
裡面的內容:
早餐,60,飲食
writer.write() 是什麼?
這行:
writer.write("早餐,60,飲食");
代表:
把這段文字寫進檔案。
如果想換行,可以加入:
"\n"
例如:
writer.write("早餐,60,飲食\n");
writer.write("午餐,150,飲食\n");
writer.write("捷運,30,交通\n");
檔案就會變成:
早餐,60,飲食
午餐,150,飲食
捷運,30,交通
為什麼使用逗號?
今天我打算讓每一筆資料使用:
項目,金額,分類
例如:
早餐,60,飲食
電影,300,娛樂
捷運,30,交通
這樣之後讀取檔案時,就可以把:
早餐,60,飲食
重新拆成:
早餐
60
飲食
Day 22 就會使用這些資料重新建立 Expense Object。
把 ArrayList 裡的資料全部寫入檔案
目前所有記帳資料都在:
expenses
所以可以使用 for:
for (int i = 0; i < expenses.size(); i++) {
Expense expense = expenses.get(i);
writer.write(
expense.getItem() + ","
+ expense.getAmount() + ","
+ expense.getCategory() + "\n"
);
}
假設 ArrayList 裡面有:
早餐|60|飲食
午餐|150|飲食
捷運|30|交通
程式就會寫成:
早餐,60,飲食
午餐,150,飲食
捷運,30,交通
建立 saveExpenses() Method
今天把儲存功能獨立成:
public static void saveExpenses()
內容:
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.getItem() + ","
+ expense.getAmount() + ","
+ expense.getCategory() + "\n"
);
}
writer.close();
System.out.println("記帳資料已儲存!");
} catch (IOException e) {
System.out.println("檔案儲存失敗!");
}
}
writer.close() 很重要
寫完檔案之後:
writer.close();
代表把檔案關閉。
可以簡單理解成:
開啟檔案
↓
寫入資料
↓
寫入完成
↓
關閉檔案
所以基本流程是:
FileWriter writer =
new FileWriter("expenses.txt");
writer.write("資料");
writer.close();
今天修改主選單
昨天有:
今天加入:
所以變成:
===== 個人記帳系統 =====
Day 21 完整程式
今天的完整版本:
import java.util.Scanner;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.io.FileWriter;
import java.io.IOException;
class Expense {
private String item;
private int amount;
private String category;
Expense(String item, int amount, String category) {
this.item = item;
this.amount = amount;
this.category = category;
}
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) {
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:
saveExpenses();
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("===== 新增消費 =====");
System.out.print("請輸入消費項目:");
String item = scanner.nextLine();
System.out.print("請輸入消費金額:");
int amount = scanner.nextInt();
String category = chooseCategory();
Expense expense =
new Expense(item, amount, category);
expenses.add(expense);
System.out.println("記帳成功!");
}
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.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() {
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);
System.out.println(
"已刪除:" + deleted.getItem()
);
} else {
System.out.println("沒有這筆紀錄!");
}
}
public static void editExpense() {
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);
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 saveExpenses() {
System.out.println();
System.out.println("===== 儲存記帳資料 =====");
try {
FileWriter writer =
new FileWriter("expenses.txt");
for (int i = 0; i < expenses.size(); i++) {
Expense expense = expenses.get(i);
writer.write(
expense.getItem() + ","
+ expense.getAmount() + ","
+ expense.getCategory() + "\n"
);
}
writer.close();
System.out.println("記帳資料已儲存!");
} catch (IOException e) {
System.out.println("檔案儲存失敗!");
}
}
}
實際執行看看
假設我新增:
===== 消費紀錄 =====
接著選擇:
請選擇功能:9
程式顯示:
===== 儲存記帳資料 =====
記帳資料已儲存!
這時候就會產生:
expenses.txt
打開檔案,可以看到:
早餐,60,飲食
午餐,150,飲食
捷運,30,交通
電影,300,娛樂
這表示資料真的已經寫進檔案裡。
但現在還有一個問題
雖然今天已經成功:
ArrayList
↓
expenses.txt
但是重新啟動程式後:
expenses.txt
✕
ArrayList
程式還不知道怎麼把檔案裡的資料讀回來。
所以現在:
關閉程式
↓
檔案還在 ✓
↓
重新開啟程式
↓
ArrayList 還是空的
這就是明天要解決的問題。
Day 21 學習心得
今天第一次把 Java 的資料真正寫入檔案。以前的記帳資料都只存在 ArrayList 裡,只要程式結束就會消失;加入 FileWriter 之後,現在已經可以把每筆消費保存到 expenses.txt。
這次也發現前面學過的 Exception Handling 不只可以處理使用者輸入錯誤,在檔案操作時同樣很重要,因為讀寫檔案也有可能發生問題。
目前雖然已經可以把資料存進檔案,但重新開啟程式後還不會自動讀取,所以今天只完成了「存檔」的一半。下一步就是把保存的資料重新載入 ArrayList。