前言
之前以為Java和C#中訪問修飾符差不多一樣,後面才瞭解到還是有些差異,本節只講解學習Java中訪問修飾符一些需要注意的地方或者從概念上不太好理解我們會通過實際例子來說明,若有錯誤之處,還請批評指正。
訪問修飾符
Java預設(Default)訪問修飾符許可權和C#中類似(在C#中準確的說巢狀在其他類中預設可以為私有,如果直接在名稱空間宣告的類或結構體可以是public,也可以是internal,但一定不是私有,這裡請注意),如果不提供任何訪問修飾符,那麼該類將具有包訪問許可權,比如如下在包com.company下建立不帶訪問修飾符的Person,然後再在該包中建立Person1,在該類中我們可以初始化Person
package com.company; class Person { }
package com.company; public class Person1 { public void InitialPerson(){ new Person(); } }
但是接下來我們再建立一個包com.company1,在其包下通過Person2則不能初始化Person,此時必然會出現編譯錯誤
package com.company1; import com.company.Person; public class Person2 { public void InitialPerson(){ new Person(); } }
對於private私有修飾符則沒有太多要講解的了,除了包含該成員的類外,其他任何類都無法訪問此類成員,Java中比較難理解的是protected修飾符, 該訪問修飾符無論是Java還是C#主要用來處理繼承的概念,憑藉我們對C#的理解,這裡我們認為是本包任何類以及實現該類的子類(不管子類是否在本包中還是其他包中) ,這種說法是完全正確的嗎?子類在同一包中肯定可以訪問,我們討論在不同包中的情況會略顯複雜一些,首先我們在包com.company定義Animal類,而在包com.company1中定義Tiger類,同時定義一個訪問修飾符為protected的動物叫的方法如下:
package com.company; public class Animal { protected void Shout(){ System.out.println("Animal"); } }
package com.company1; import com.company.Animal; public class Tiger extends Animal { }
接下來我們再在包com.company中定義一個類,然後在該類中定義方法,將上述位於不同包中的Animal和Tiger類作為引數變數,此時可以訪問叫的方法
package com.company; import com.company1.Tiger; public class OtherAnimal { public void OtherMethod(Animal animal, Tiger tiger) { animal.Shout(); tiger.Shout(); } }
然後我們再在Tiger子類定義一個方法,無論是通過當前例項引用還是直接通過super關鍵字呼叫,都可以呼叫基類的叫方法
package com.company; import com.company1.Tiger; public class OtherAnimal { public void OtherMethod(Animal animal, Tiger tiger) { animal.Shout(); tiger.Shout(); } }
當然若我們在基類Animal中定義方法通過其基類變數訪問叫的方法毫無疑問也是可以的,即使將叫的方法設定為私有的,因為在其基類內部
public void AnimalMethod(Animal animal) { animal.Shout(); }
若我們在子類Tiger中再定義一個方法,將Tiger作為變數傳遞進去,此時也是可以訪問基類的叫方法
public void tigerMethod1(Tiger tiger) { tiger.Shout(); }
已經列舉如上諸多情況,那是不是就說明在子類中一定能訪問到叫方法呢?當然不是,如下兩種情況則是無效的,會出現編譯錯誤。
當在基類包外直接引用基類變數訪問無效
我們在子類中再定義一個方法,直接引用基類的變數,然後訪問叫方法,此時將無效。因為protected具有包訪問許可權,使得直接訪問基類受保護成員變為私有或者說直接引用基類變數,無法判斷其型別,因為可能在執行時是基類中的其他子型別,這麼講是否會更妥當一點。
public void tigerMethod2(Animal animal) { //發生編譯錯誤 animal.Shout(); }
當在子類包中的非子類直接引用子類變數訪問無效
我們在子類所在包中再定義一個類,然後引用子類變數訪問叫方法,此時將無效。因為子類從其基類繼承受保護的成員,此時會使它們對非子類私有:
package com.company1; public class Tiger1 { public void tiger1Method(Tiger tiger){ //發生編譯錯誤 tiger.Shout(); } }
總結
網上有一部分文章對protected的總結是:本包任何類以及實現該類的子類(不管子類是否在本包中還是其他包中),這種說法不能說錯誤,只能說意思比較隱晦,因為直接讀這段話可能就只考慮了在子類中通過子類例項或直接通過關鍵字super呼叫基類的受保護的成員,而可能會欠缺對非子類和直接通過基類引用受保護的成員的考慮。 protected修飾符無論是Java亦或是C#具有兩層概念訪問許可權的組合,一是基於程式集(C#)、包(Java)許可權、二是繼承許可權。所以對於Java的protected訪問許可權可總結為:本包任何類以及實現該類的子類,無論子類位於本包還是不同包,但對基類外部包直接訪問私有,同時對外部包中非子類私有。