iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
自我挑戰組

Java 程式設計與實作:30 天打造個人記帳系統系列 第 21 篇

File 檔案寫入:讓記帳紀錄可以真正保存

  • 分享至 

  • xImage
  •  

昨天完成了「預算與超支提醒」功能,目前記帳系統已經可以新增、查看、修改、刪除、統計消費,也可以設定預算。

不過目前還有一個很大的問題。

假設今天輸入:

  1. 早餐|60 元|飲食
  2. 午餐|150 元|飲食
  3. 捷運|30 元|交通

只要把 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();

今天修改主選單

昨天有:

  1. 新增消費
  2. 查看紀錄
  3. 查看總支出
  4. 分類支出統計
  5. 刪除消費紀錄
  6. 修改消費紀錄
  7. 設定預算
  8. 查看預算狀況
  9. 離開

今天加入:

  1. 儲存記帳資料
  2. 離開

所以變成:

===== 個人記帳系統 =====

  1. 新增消費
  2. 查看紀錄
  3. 查看總支出
  4. 分類支出統計
  5. 刪除消費紀錄
  6. 修改消費紀錄
  7. 設定預算
  8. 查看預算狀況
  9. 儲存記帳資料
  10. 離開

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("檔案儲存失敗!");
}
}
}

實際執行看看

假設我新增:

===== 消費紀錄 =====

  1. 早餐|60 元|飲食
  2. 午餐|150 元|飲食
  3. 捷運|30 元|交通
  4. 電影|300 元|娛樂

接著選擇:

請選擇功能:9

程式顯示:

===== 儲存記帳資料 =====
記帳資料已儲存!

這時候就會產生:

expenses.txt

打開檔案,可以看到:

早餐,60,飲食
午餐,150,飲食
捷運,30,交通
電影,300,娛樂

這表示資料真的已經寫進檔案裡。

但現在還有一個問題

雖然今天已經成功:

ArrayList
↓
expenses.txt

但是重新啟動程式後:

expenses.txt
✕
ArrayList

程式還不知道怎麼把檔案裡的資料讀回來。

所以現在:

關閉程式
↓
檔案還在 ✓
↓
重新開啟程式
↓
ArrayList 還是空的

這就是明天要解決的問題。

Day 21 學習心得

今天第一次把 Java 的資料真正寫入檔案。以前的記帳資料都只存在 ArrayList 裡,只要程式結束就會消失;加入 FileWriter 之後,現在已經可以把每筆消費保存到 expenses.txt。

這次也發現前面學過的 Exception Handling 不只可以處理使用者輸入錯誤,在檔案操作時同樣很重要,因為讀寫檔案也有可能發生問題。

目前雖然已經可以把資料存進檔案,但重新開啟程式後還不會自動讀取,所以今天只完成了「存檔」的一半。下一步就是把保存的資料重新載入 ArrayList。


上一篇
記帳系統實作:加入「消費預算」與超支提醒
下一篇
File 檔案讀取:重新開啟程式也能看到以前的記帳紀錄
系列文
Java 程式設計與實作:30 天打造個人記帳系統 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言