iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
自我挑戰組

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

Encapsulation 封裝:保護記帳資料

  • 分享至 

  • xImage
  •  

昨天學習了 Constructor 建構子,讓我們可以在建立 Expense 物件的同時,直接放入消費項目、金額等資料。

目前的 Expense 類別大概是這樣:

class Expense {
String item;
int amount;
String category;
Expense(String item, int amount, String category) {
this.item = item;
this.amount = amount;
this.category = category;
}
}

雖然已經可以使用,但這些資料可以直接從外部修改。

例如:

expense.amount = -500;

這樣就可能出現不合理的消費金額。

所以今天要學 Java 物件導向的另一個重要觀念:Encapsulation(封裝)。

什麼是 Encapsulation?

封裝可以簡單理解成:

把物件內部的資料保護起來,不讓外部程式隨便修改。

在 Java 中,可以使用 private:

private String item;
private int amount;
private String category;

當變數設定成 private 之後,就不能直接在其他地方寫:

expense.amount = 100;

而是要透過指定的方法來取得或修改資料。

private 是什麼?

之前我們寫:

String item;
int amount;

今天改成:

private String item;
private int amount;

private 代表這些變數主要只能在自己的 Class 裡直接使用。

例如:

class Expense {
private String item;
private int amount;
}

如果 Main 想直接使用:

expense.item

就會出現錯誤。

因此我們需要另外建立方法,讓外部程式可以安全地取得資料。

Getter:取得資料

Getter 可以用來取得物件裡面的資料。

例如:

public String getItem() {
return item;
}

以及:

public int getAmount() {
return amount;
}

之後就可以使用:

expense.getItem();
expense.getAmount();

例如:

System.out.println(expense.getItem());

就可以取得消費項目。

Setter:修改資料

如果希望允許修改資料,可以建立 Setter。

例如:

public void setItem(String item) {
this.item = item;
}

使用:

expense.setItem("晚餐");

就可以修改消費項目。

金額也可以建立:

public void setAmount(int amount) {
this.amount = amount;
}

不過 Setter 還有一個很實用的地方,就是可以加入條件判斷。

例如:

public void setAmount(int amount) {
if (amount >= 0) {
this.amount = amount;
} else {
System.out.println("金額不能小於 0!");
}
}

這樣如果有人輸入:

expense.setAmount(-500);

程式就不會直接接受這個不合理的金額。

這也是封裝的好處之一。

今天的小實作:封裝 Expense

今天把之前的 Expense 修改成:

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;
} else {
System.out.println("金額不能小於 0!");
}
}
public void setCategory(String category) {
this.category = category;
}
}

現在 item、amount 和 category 都變成 private。

修改原本的記帳系統

因為變數變成 private,所以之前這種寫法:

expense.item
expense.amount

就不能再使用。

要改成:

expense.getItem()
expense.getAmount()

完整程式可以整理成:

import java.util.Scanner;
import java.util.ArrayList;
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;
} else {
System.out.println("金額不能小於 0!");
}
}
public void setCategory(String category) {
this.category = category;
}
}
public class Main {
static Scanner scanner = new Scanner(System.in);
static ArrayList expenses = new ArrayList<>();
public static void main(String[] args) {
boolean running = true;
while (running) {
showMenu();
System.out.print("請選擇功能:");
int choice = scanner.nextInt();
scanner.nextLine();
switch (choice) {
case 1:
addExpense();
break;
case 2:
showExpenses();
break;
case 3:
running = false;
System.out.println("已離開個人記帳系統!");
break;
default:
System.out.println("輸入錯誤,請重新選擇!");
}
}
scanner.close();
}
public static void showMenu() {
System.out.println();
System.out.println("===== 個人記帳系統 =====");
System.out.println("1. 新增消費");
System.out.println("2. 查看紀錄");
System.out.println("3. 離開");
}
public static void addExpense() {
System.out.print("請輸入消費項目:");
String item = scanner.nextLine();
System.out.print("請輸入消費金額:");
int amount = scanner.nextInt();
scanner.nextLine();
System.out.print("請輸入消費分類:");
String category = scanner.nextLine();
Expense expense =
new Expense(item, amount, category);
expenses.add(expense);
System.out.println("記帳成功!");
}
public static void showExpenses() {
System.out.println();
System.out.println("===== 消費紀錄 =====");
if (expenses.size() == 0) {
System.out.println("目前沒有消費紀錄。");
} else {
for (int i = 0; i < expenses.size(); i++) {
Expense expense = expenses.get(i);
System.out.println(
(i + 1) + ". "
+ expense.getItem()
+ "|" + expense.getAmount() + " 元"
+ "|" + expense.getCategory()
);
}
}
}
}

實際執行看看

新增一筆消費:

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

  1. 新增消費
  2. 查看紀錄
  3. 離開
    請選擇功能:1
    請輸入消費項目:午餐
    請輸入消費金額:120
    請輸入消費分類:飲食
    記帳成功!

再新增一筆:

請輸入消費項目:捷運
請輸入消費金額:30
請輸入消費分類:交通
記帳成功!

查看紀錄:

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

  1. 午餐|120 元|飲食
  2. 捷運|30 元|交通

雖然使用者看到的結果沒有太大變化,但是程式內部的資料已經受到保護。

Getter 和 Setter 怎麼記?

今天一次出現很多新的名稱,我覺得可以用最簡單的方法記。

Getter = Get = 取得資料

expense.getAmount();

意思就是:

我要取得這筆消費的金額。

Setter = Set = 設定或修改資料

expense.setAmount(200);

意思就是:

我要把這筆消費的金額設定成 200。

而:

private int amount;

則代表:

不希望外面的程式直接亂改 amount。

所以整體可以理解成:

private
↓
保護資料
↓
Getter → 讀取資料
Setter → 修改資料

Day 13 學習心得

今天學習了 Java 物件導向中的 Encapsulation 封裝。一開始看到 private、Getter 和 Setter 覺得程式碼反而變多了,但實際理解之後,發現封裝的目的不是單純讓程式變複雜,而是讓資料的存取方式更有規則。

把消費項目、金額和分類設定成 private 之後,外部程式不能直接修改,而是需要透過 Getter 和 Setter 操作。Setter 裡還可以加入條件判斷,避免不合理的資料被存進物件。

目前從 Class、Object、Constructor 到 Encapsulation,我也開始比較能理解 Java 為什麼會強調物件導向。


上一篇
Constructor 建構子:建立物件時直接加入消費資料
下一篇
Inheritance 繼承:建立不同類型的消費紀錄
系列文
Java 程式設計與實作:30 天打造個人記帳系統 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言