@author: Tobin
Java初學者,試圖用最簡單的大白話讓自己搞懂一些知識點。
修飾符modifiers介紹
學習Java不可避免地接觸到一些基本的修飾符。
修飾符決定了類成員的訪問權限,是否能夠被其它類所訪問。
- private: 只能被基類訪問
- 無修飾符: 被基類,子類和同package的類訪問
- protected: 在無修飾符的基礎上,加了與基類不同包,但是是子類的訪問權限,這個訪問權限只在子類訪問自身的實例時才有,超類的實例還是不能訪問的
- public: 全都可以訪問
下面這個很好地展現了權限的逐層增加。
protected修飾符詳解
《Java編程思想》對protected的介紹是:被protected修飾的成員對於本包和其子類可見。看了上面的圖,我們知道子類可見的意思是不同包的子類也是可見的。總結一下就是:
- 基類的protected成員是包內可見的
- 當不在同一個包,但是是其子類也是可以訪問protected的成員的。但是有個前提,就是子類只能訪問其繼承來的protected成員,如果是在子類中初始化一個超類的實例,這個實例是無法訪問protected的成員的。
概念比較抽象,用形象的語言來說。
包就相當於一個家族。不同包有可能有繼承的分支,也有可能毫無關系。類內的成員就相當於資源。 - private: 私有資源,只能自己使用
- default: 家族,我自己,我子孫可以用
- protected: 我可以開放,但是只給自己家族的但是嫁到或者入贅其它家族的后代使用
- public: 誰都可以用,水資源
參考幾個例子,分析下。
(1)示例1
//示例一
package p1;
public class Father1 {
protected void f() {} // 父類Father1中的protected方法
}
package p1;
public class Son1 extends Father1 {}
package p11;
public class Son11 extends Father1{}
package p1;
public class Test1 {
public static void main(String[] args) {
Son1 son1 = new Son1();
son1.f(); // Compile OK ----(1)
son1.clone(); // Compile Error ----(2)
Son11 son = new Son11();
son11.f(); // Compile OK ----(3)
son11.clone(); // Compile Error ----(4)
}
}
- f方法是超類Father1的,son1和son11雖然是定義不同的包,但是它們的都是Father1的子類,所以都可以訪問f方法。此外Test1也在p1包里,所以1和3編譯通過。
- son1和son11的clone()方法都是來自於Father1,但是和f的區別在於,Father的clone()還來自於java.lang.Object,Test1也是Object的子類,這只是說明它可以訪問自己繼承的clone()方法,即使其它分支繼承了相同的方法,它也是不可以訪問的。想象一下,家族給其它分支都分配一些資源,即使是相同的,我也不該有權限去訪問其它分支的資源。
(2)示例2
//示例二
package p2;
class MyObject2 {
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
package p22;
public class Test2 extends MyObject2 {
public static void main(String args[]) {
MyObject2 obj = new MyObject2();
obj.clone(); // Compile Error ----(1)
Test2 tobj = new Test2();
tobj.clone(); // Complie OK ----(2)
}
}
- Test2繼承了MyObject2,clone()方法來源於MyObject2,Test2可以建立自身實例,然后訪問clone()方法,2通過
- 但是作為子類,Test2建立超類實例,直接去訪問超類的protected方法是不可以的
- 想一下,家族已經給我分支資源了,我還要直接向家族拿資源,不該有這個權限
(3)示例3
//示例三
package p3;
class MyObject3 extends Test3 {
}
package p33;
public class Test3 {
public static void main(String args[]) {
MyObject3 obj = new MyObject3();
obj.clone(); // Compile OK ------(1)
}
}
- MyObject3繼承Test3,兩個不在一個包內。Test3作為超類,建立了子類的一個實例,訪問子類繼承的來自於它的方法,當然它的clone()方法,也是繼承自java.lang.Object,這個不管,繼承自我的,我是有權限訪問的
- 想象一下,家族給分支資源了,家族是有權限直接訪問我給出的資源的
(4)示例4
//示例四
package p4;
class MyObject4 extends Test4 {
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package p44;
public class Test4 {
public static void main(String args[]) {
MyObject4 obj = new MyObject4();
obj.clone(); // Compile Error -----(1)
}
}
- 和示例3的區別在於,此時的clone()方法是子類自身的方法了,來源於MyObject4本身,作為超類是沒有權限訪問子類的protected成員的
- 想象一下,分支建立自己的受保護資源,家族沒有權限直接訪問的
(5)示例5
//示例五
package p5;
class MyObject5 {
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Test5 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject5 obj = new MyObject5();
obj.clone(); // Compile OK ----(1)
}
}
- 現在屬於同一個包,Test5可以直接訪問Myobject5的protected成員
- 想象一下,同一個家族的資源共享
(6)示例6
//示例六
package p6;
class MyObject6 extends Test6{}
public class Test6 {
public static void main(String[] args) {
MyObject6 obj = new MyObject6();
obj.clone(); // Compile OK -------(1)
}
}
- 即使不在同一個包,1也成立。此處在同一個包,且clone()方法來自於Test6
- 同一個家族的資源共享
(7)示例7
//示例七
package p7;
class MyObject7 extends Test7 {
public static void main(String[] args) {
Test7 test = new Test7();
test.clone(); // Compile Error ----- (1)
}
}
public class Test7 {}
- 超類Test7的方法來自於java.lang.Object,只有java.lang這個包和對應繼承了這個clone()方法的Test7才能訪問。說白了,方法還是一個方法,但是被不同的子類繼承了,就不再是同樣的方法了。
- 看起來是一個家族的,實際上資源是超類從其它家族得到的。其它家族對該資源做了限制,所以不能整個家族都能使用。其次,子類只能訪問自身實例的protected資源,沒有權限訪問超類實例的protected資源。兩個條件都不滿足。
其它修飾符
static
final
abstract
見我Java基礎知識系列的其它文章。
總結
protected成員,在相同package下,對其它類開放,不同package下,對繼承了該類的子類開放(有條件)。其它包的子類沒有權限直接創建超類的實例,然后訪問超類的protected成員。除了認清楚protected屬性,更重要的是要辨別來源,來源決定package是哪個,這是決定訪問權限的基礎。
參考文章
https://blog.csdn.net/ciawow/article/details/8262609。
https://blog.csdn.net/justloveyou_/article/details/61672133