看似簡單的東西可以引出很多問題,學習過程中很多概念我們都只是「好像瞭解」、「貌似是這樣」、「應該沒問題」, 其實缺乏的是仔細思考, 對自己少問了幾個「為什麼」。
在 Java 中, 訪問許可權修飾符屬於最最基礎的知識, protected 修飾符只是其中一個, 如果你要問為什麼不拿 public、default、private 來深究呢? 那麼看完這篇文章你會知道為何 protected 更值得深入️思考。
在 《Thinking in Java》 中,protected 的名稱是「繼承訪問許可權」,這也就是我們記憶中的 protected:protected 必須要有繼承關係才能夠訪問。 所以你以為你懂了, 可是你真的理解了這句話嗎?
先思考幾個問題:
同一個包中, 子類物件能訪問父類的 protected 方法嗎?
不同包下, 在子類中建立該子類物件能訪問父類的 protected 方法嗎?
不同包下, 在子類中建立父類物件能訪問父類的 protected 方法嗎?
不同包下, 在子類中建立另一個子類的物件能訪問公共父類的 protected 方法嗎?
父類 protected 方法加上 static 修飾符又會如何呢?
《Thinking in Java》中有一句話:「protected 也提供包訪問許可權, 也就是說,相同包內的其他類可以訪問 protected元素」, 其實就是 protected 修飾符包含了 default 預設修飾符的許可權, 所以第 1 個問題你已經知道答案了, 在同一個包中, 普通類或者子類都可以訪問基類的 protected 方法。
父類為非靜態 protected 修飾類
package com.protectedaccess.parentpackage;
public class Parent {
protected String protect = "protect field";
protected void getMessage(){
System.out.println("i am parent");
}
}複製程式碼
不同包下,在子類中通過父類引用不可以訪問其 protected 方法
無論是建立 Parent 物件還是通過多型建立 Son1 物件, 只要 Parent 引用, 則不可訪問, 編譯器會提示錯誤。
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class Son1 extends Parent{
public static void main(String[] args) {
Parent parent1 = new Parent();
// parent1.getMessage(); 錯誤
Parent parent2 = new Son1();
// parent2.getMessage(); 錯誤
}
}複製程式碼
不同包下,在子類中通過該子類引用可以訪問其 protected 方法
子類中實際上把父類的方法繼承下來了, 可以通過該子類物件訪問, 也可以在子類方法中直接訪問, 還可以通過 super 關鍵字呼叫父類中的該方法。
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class Son1 extends Parent{
public static void main(String[] args) {
Son1 son1 = new Son1();
son1.getMessage(); // 輸出:i am parent,
}
private void message(){
getMessage(); // 如果子類重寫了該方法, 則輸出重寫方法中的內容
super.getMessage(); // 輸出父類該方法中的內容
}
}複製程式碼
不同包下,在子類中不能通過另一個子類引用訪問共同基類的 protected 方法
package com.protectedaccess.parentpackage.sonpackage2;
import com.protectedaccess.parentpackage.Parent;
public class Son2 extends Parent {
}複製程式碼
注意是 Son2 是另一個子類, 在 Son1 中建立 Son2 的物件是無法訪問父類的 protected 方法的
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
import com.protectedaccess.parentpackage.sonpackage2.Son2;
public class Son1 extends Parent{
public static void main(String[] args) {
Son2 son2 = new Son2();
// son2.getMessage(); 錯誤
}
}複製程式碼
父類為靜態 protected 修飾類
對於protected的靜態變數, 在子類中可以直接訪問, 在不同包的非子類中則不可訪問
package com.protectedaccess.parentpackage;
public class Parent {
protected String protect = "protect field";
protected static void getMessage(){
System.out.println("i am parent");
}
}複製程式碼
靜態方法直接通過類名訪問
無論是否同一個包,在子類中均可直接訪問
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class Son3 extends Parent{
public static void main(String[] args) {
Parent.getMessage(); // 輸出: i am parent
}
}複製程式碼
在不同包下,非子類不可訪問
package com.protectedaccess.parentpackage.sonpackage1;
import com.protectedaccess.parentpackage.Parent;
public class Son4{
public static void main(String[] args) {
// Parent.getMessage(); 錯誤
}
}複製程式碼
看到這裡你應該知道有多少種情況了, 針對不同的情況都可能出現意外的結果, 所以還是得多實踐, 僅僅在書上看一遍 protected 修飾符的作用是無法真正發現它的微妙。