原連結
前言
JavaGuide中關於繼承如下 3 點請記住:
- 子類擁有父類物件所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類是無法訪問,只是擁有。
嘗試驗證這一觀點。
物件初始化
首先從物件初始化開始思考,一般繼承某個父類的子類物件初始化時是按照以下順序:
- 父類的靜態變數和常量以及父類的靜態程式碼塊
- 子類的靜態變數和常量以及子類的靜態程式碼塊
- 父類的變數賦預設值和父類程式碼塊
- 父類的構造方法
- 子類的變數賦預設值和父類程式碼塊
- 子類的構造方法
既然按照物件初始化順序來說,子類的構造方法被呼叫的時候必然有一個父類物件被構造,那麼必然有辦法在子類構造時對父類的變數進行賦值。
Java如果不指定構造方法,會預設給每個類提供一個無參構造方法,那如果我的父類只有有參構造方法又會如何呢?
使用有參構造方法
首先準備好了父類,這個父類裡面只有一個parentName
的屬性,同時制定一個有參構造方法,程式碼如下:
public class Parent {
private String parentName;
public Parent(String parentName) {
this.parentName = parentName;
}
}
然後準備了子類繼承父類,這個時候有意思的事情發生了,子類必須提供一個帶參的構造方法來構造父類物件,同時要在這個帶參構造方法裡面透過super關鍵字呼叫父類的構造方法,否則編譯會失敗,最終程式碼如下:
public class Child extends Parent {
public Child(String parentName) {
super(parentName);
}
}
其實從這裡就可以知道子類是擁有父類的私有屬性的了,只是因為訪問控制限制無法直接訪問這個屬性。
但是除了這個方法以外,我還有什麼辦法去佐證這個觀點呢?當然有啦,而且還不止一種呢,我知道的就有兩個辦法:
- 直接檢視位元組碼
- 使用反射獲取
下面展示如何使用反射獲取父類的屬性的示例程式碼:
Child child = new Child("David");
try {
// 獲取父類Class物件
Class parentClass = child.getClass().getSuperclass();
// 獲取父類的屬性
Field field = parentClass.getDeclaredField("parentName");
// 解除訪問限制
field.setAccessible(true);
// 輸出
System.out.println(field.get(child));
} catch (NoSuchFieldException e) {
// 這裡只是演示用,實際開發時需要將錯誤資訊列印到日誌檔案
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
輸出結果:
David