項目13:最小化類別和成員的存取權限
資訊隱藏或封裝(information hiding or encapsulation)
一個好的模組設計會隱藏所有實作的細節, 模組間的溝通皆透過API.
Java的存取控制機制(access control mechanism)透過修飾詞(access modifiers:private, protected and public), 在宣告類別(class),介面(interface)和成員時定義其存取權限. 經驗法則很簡單:每個類別或是成員間的存取權限越小越好.
最上層的類別和介面: package-private, public
假設最上層的類別或介面不宣告為public, 則為package-private. 如果最上層的類別或介面可以是package-private, 就不該宣告為public, 盡可能將其歸為實作的一部分而非外露的API, 如此一來, 在修改, 取代或是刪除時不會對既存的用戶造成影像.
假如只有一個類別使用為package-private的最上層類別或介面, 則應該將其宣告為該類別內private的巢狀類別(nested class), 避免其他類別去使用.
但相較於於最上層package-private的問題, 更重要的是減少最上層public的
類別, public 類別是API的一部分, 而package-private 最上層類別則是它實作的部分.
成員(fields, methods, nested classed and nested interfaces)的存取階層
- private 只能在其宣告的類別存取.
- package-private 預設修飾詞, 相同package皆可存取.
- protected 子類別才可存取.
- public 全部
反射性下, 你應該將成員皆宣告為private, 除非在同一個package的成員真的需要存取它的時候, 才將其宣告為package-private, 但如果你常常做這種異動, 你可能需要重新審視你系統的設計, 是否將類別做適當地切割(decouple).
protected的成員是外露API的ㄧ部分, 更是將其實作的細節揭露, 使用protected修飾詞的情況應相對稀少.
假如覆寫了父類別的method, 其修飾詞的權限不可小於父類別, 在可使用父類別的地方, 應該可以使用子類別, 如果違反此規則, compile會失敗. 唯一特例是介面(interface), 介面的method預設都是public, 所以實作的類別覆寫的method也是public.
實例屬性(instance field)不可為public, 如果一個實例屬性不是final, 或者是final但指向一個mutable的物件, 宣告為public時, 你就放棄了維持該屬性的不變性. 而類別中有public且mutable屬性時, 非thread-safe. 靜態屬性同上, 但有單一例外, 將常用的常數宣告成public static final, 方便存取, 且這些常數通常為immutable物件, 宣告為大寫且用底線分隔詞彙.
長度非0的陣列為mutable, 不可將其宣告為public static final
// Potential security hole!
public static final Thing[] VALUES = { ... };
解決方案一:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
解決方案二:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}