提到 protected 訪問修飾符,即便是初學者往往也自以為理解透徹。但若提出一個具體場景,許多人可能就會卡住。我們來看一個經典案例:
假設有兩個包:testa 和 testb。在包 A 中定義一個父類 Animal,包含一個 protected 方法 crowl(String);在包 B 中,Cat 和 Rat 兩個子類分別繼承 Animal。
// 包 A:父類 Animal
package testa;
public class Animal {
protected void crowl(String c) {
System.out.println(c);
}
}
// 包 B:子類 Cat 和 Rat
package testb;
import testa.Animal;
class Cat extends Animal { }
class Rat extends Animal {
public void crowl() {
crowl("zhi zhi"); // ✅ 正常:調用自己繼承的 protected 方法
Cat cat = new Cat();
cat.crowl("miao miao"); // ❌ 編譯錯誤:The method crowl(String) from the type Animal is not visible
}
}
Cat 和 Rat 都是 Animal 的直接子類,為什麼在 Rat 的作用域內,無法訪問 Cat 實例繼承來的 crowl(String) 方法?
protected 的訪問規則極其微妙。雖然 protected 成員對所有子類可見,但有一條鐵律:
子類只能在自己的作用域內訪問自己繼承的父類 protected 成員,而無法訪問其他子類(即便是同父類的「親兄弟」)所繼承的 protected 成員。
通俗來說:老鼠只能叫「吱吱」。哪怕它能看見貓(在自己的作用域內創建 Cat 對象),也永遠學不會貓叫「喵喵」。
具體來說,Cat 繼承的 crowl 方法在 Cat 類作用域內可見,但在 Rat 類作用域內不可見——即使兩者是親兄弟關係也不行。
這正是我們在使用 clone() 方法時,不能簡單地直接調用 aObject.clone() 的根本原因。Object.clone() 是 protected 的,不同類之間無法互相調用對方繼承來的 clone(),必須在自身類中重寫並聲明為 public。
當 B extends A 時,在子類 B 的作用域內,只能調用本子類 B 定義的對象的 protected 方法(該方法從父類 A 繼承而來),而不能調用其他 A 類對象(包括其他子類實例)的 protected 方法。