深入理解 Java 中 protected 修飾符

leozzy發表於2017-12-01

看似簡單的東西可以引出很多問題,學習過程中很多概念我們都只是「好像瞭解」、「貌似是這樣」、「應該沒問題」, 其實缺乏的是仔細思考, 對自己少問了幾個「為什麼」。

在 Java 中, 訪問許可權修飾符屬於最最基礎的知識, protected 修飾符只是其中一個, 如果你要問為什麼不拿 public、default、private 來深究呢? 那麼看完這篇文章你會知道為何 protected 更值得深入️思考。

在 《Thinking in Java》 中,protected 的名稱是「繼承訪問許可權」,這也就是我們記憶中的 protected:protected 必須要有繼承關係才能夠訪問。 所以你以為你懂了, 可是你真的理解了這句話嗎?

先思考幾個問題:

  1. 同一個包中, 子類物件能訪問父類的 protected 方法嗎?

  2. 不同包下, 在子類中建立該子類物件能訪問父類的 protected 方法嗎?

  3. 不同包下, 在子類中建立父類物件能訪問父類的 protected 方法嗎?

  4. 不同包下, 在子類中建立另一個子類的物件能訪問公共父類的 protected 方法嗎?

  5. 父類 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 修飾符的作用是無法真正發現它的微妙。

相關文章