前兩天我們學會了用 new 壓模印出身分證實體,並用建構子(Constructor)在出爐瞬間把姓名、年齡等初始資料寫進卡片裡。
但如果沒有特別設防,卡片上的欄位預設都是完全是對外開放的。這意味著外人只要拿到你的證件夾(變數 p1),就能直接用點運算子 . 伸手進去為所欲為:
Person p1 = new Person("王小明", 20);
p1.age = -50; // 年齡變成負五十歲?!
現實生活中,哪有人是負五十歲的?你絕不可能讓路人隨便塗改你的證件年齡。如果讓外部程式隨意竄改內部欄位,系統遲早會因為邏輯錯亂而當機崩潰。
在程式世界裡,這項防禦措施就叫做封裝(Encapsulation)「非我族類(class)皆是外人!任何人休想用 p1. 隨意偷窺或竄改」。
Getter 就是「你問問題,我給答案」的官方查詢窗口
當外界被 private 擋住、看不到卡片資訊時,想知道年齡就必須走這個合法窗口:
// 查閱窗口:外人想看年紀
public int getAge() {
return this.age; // 櫃台看這張卡片的年紀,把答案遞回去
}
這兩行程式碼的真正對話其實是這樣的:
public int getAge()(我想看年紀):
外人站在櫃台前發問:「我想知道年齡是多少?」因為這個動作是為了取得一個整數答案,所以開頭必須寫明 int。
return this.age;(給你一個答案):
窗口人員轉過身,依照 this.age 鎖定這張卡片身上的年齡欄位,最後透過 return 把答案正式交到你手裡。
Setter 就是「官方改單窗口」:外人下指令,櫃台代為動態賦值
Setter就是外人不能自己動手,所以Class 特地開了一個合法的外人修改管道。
public void setAge(int newAge) {
this.age = newAge;
}
外人(主程式)發動指令:
想改資料,必須指名道姓寫下 p1.setAge(25);。意思是:「我想動 p1 這張卡片,把它裡面的年齡改成括號裡的 25!」
電腦順著 p1 找到它所屬的 Person 類別,感應發現外部呼叫的是 setAge,而且括號裡丟的是一個整數 25。它精準命中規格為 (int newAge) 的窗口,把數字接了進去。
櫃台代勞,內部賦值:
外部絕對碰不到晶片本體。由窗口人員替你執行 this.age = newAge;,把這張卡片的年齡覆蓋成新值。
用 void 做完就收工:
目的是「叫它去改」,而不是「跟它要東西」。手續辦好、改完收工,櫃台不需要多此一舉把資料遞還給你,所以開頭宣告 void(不回傳任何東西)。
一句話記住 Setter:「外人在括號裡遞交新資料,櫃台代為驗收並賦值給物件。」
陪你到老的是屬性,出了大括號就斷氣的是區域變數
屬性(跟一輩子),物件出生的那一刻起,age、name 這些屬性就烙印在卡片晶片裡。
只要這個物件還在你的記憶體裡活著,這些資料就隨時跟著它不會消失。
區域變數(用完即丟):就像號碼牌
public void printDocument() {
int ticketNumber = 88; // 區域變數:這次影印拿到的號碼牌
System.out.println("拿著號碼牌 " + ticketNumber + " 去取件!");
} // 走出店門口(大括號結束),號碼牌直接丟垃圾桶!
當你啟動「影印」這個行為時,櫃台才生出一張號碼牌 ticketNumber = 88 給你;檔案印好踏出店門口(離開方法的大括號 {},這張紙片就徹底作廢、丟進垃圾桶。
下次就算再去影印一次,拿到的也是另一張全新的號碼牌,跟上次那張完全沒有任何關係。