protected 是 Java 四大存取修飾詞中最獨特、也最常被誤解的一個。它結合了「套件可見」與「子類別可見」兩種語意,專為**繼承體系**設計。本文將透過程式碼示範其精確存取規則、與 package-private 的差異,以及實務應用場景。
protected 成員可被存取的條件(滿足任一即可):
package-private)換句話說:protected = package-private + 子類別存取權。
// Cookie.java
package c05.dessert;
public class Cookie {
public Cookie() { System.out.println("Cookie constructor"); }
void foo() { System.out.println("foo"); } // package-private
}
// ChocolateChip.java
import c05.dessert.*;
public class ChocolateChip extends Cookie {
public ChocolateChip() { System.out.println("ChocolateChip constructor"); }
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
// x.foo(); // 編譯錯誤:foo() 是 package-private,跨套件子類別不可見
}
}
結果:ChocolateChip 雖繼承 Cookie,但因在不同套件,**無法存取** foo()。
// Cookie.java 修改版
public class Cookie {
public Cookie() { System.out.println("Cookie constructor"); }
protected void foo() { System.out.println("foo"); } // 改為 protected
}
現在:
c05.dessert 內類別:仍可存取 foo()(protected 包含套件可見)ChocolateChip:**可存取** foo()public)| 修飾詞 | 同套件 | 不同套件子類別 | 不同套件非子類別 | 典型用途 |
|---|---|---|---|---|
private | ✗ | ✗ | ✗ | 類別內部實作 |
package-private | ✓ | ✗ | ✗ | 套件內協作、內部實作 |
protected | ✓ | ✓ | ✗ | **給子類別的擴充點/鉤子** |
public | ✓ | ✓ | ✓ | 對外公開介面 |
protected 存在的核心理由:**基底類別設計者希望子類別能存取/覆寫特定內部機制,但不想對全世界公開**。典型場景:
protected abstract,強制子類別實作protected int state),避免 getter/setter 開銷protected void onInit() {} 讓子類別選擇性擴充初始化邏輯protected 欄位破壞封裝 → 正確:protected 針對「繼承關係」開放,比 public 窄得多,是刻意設計的擴充點private + protected 方法(而非欄位)給子類別,保留未來改變資料結構的彈性protected**,直接用 private 或 package-private@see 或說明中標註:「供子類別覆寫/存取,用於 XXX 目的」protected 是繼承體系的「特權通道」:它比 package-private 寬(跨套件子類別可見),比 public 窄(非子類別不可見)。善用它能在「封裝內部細節」與「提供擴充彈性」之間取得最佳平衡,是設計可擴充框架的關鍵工具。