摘自:https://zhidao.baidu.com/question/489562987146637292.html
下面程序的運行結果是什麼?
public class Dervied extends Base {
private String name = "dervied";
public Dervied() {
tellName();
printName();
}
public void tellName() {
System.out.println("Dervied tell name: " + name);
}
public void printName() {
System.out.println("Dervied print name: " + name);
}
public static void main(String[] args){
new Dervied();
}
}
class Base {
private String name = "base";
public Base() {
tellName();
printName();
}
public void tellName() {
System.out.println("Base tell name: " + name);
}
public void printName() {
System.out.println("Base print name: " + name);
}
}
輸出:
Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied
關鍵點:問題出在構造函數的地方,對於java中的extends,每個子類構造方法的第一條語句都是隱含的調用super,而且如果父類沒有這種形式的構造函數就會報錯。
詳細解釋:
1,首先先說一下父類與子類方法覆寫(overrider)相關的概念,覆寫在java中主要是通過方法表來完成,java中每個類型(Class)中都存在一個方法表,其中存放java類型的實例方法(public/protected),static方法屬於靜態方法,和類型相關,不屬於javad實例方法。private和default屬於私有方法,private/default修飾的方法不進入類型的方法表。
2,接下來說一下和方法,其中稱爲類初始化方法,稱爲對象實例化方法,該問題就與方法息息相關。
方法:
Java在進行對象創建時首先進行類型加載,如果Class類型存在父類型,則需要先加載父類完成以後再加載子類型。並且對static變量進行初始化操作,對static變量或者static代碼塊初始化的邏輯就封裝在方法中。
Java類型初始化過程中對static變量的初始化操作依賴與對static變量的賦值語句的前後關係,static語句塊與static變量聲明存在位置關係,java編譯器與static變量的賦值位置有關。
方法:
Java對象在進行實例化時,首先會進行父類的實例化操作,然後再進行子類的實例化操作。該部分操作封裝在方法中,並且子類的方法中會首先對父類方法的調用。
Java對象實例化過程中對實例域的初始化賦值操作全部在方法中進行,方法顯式的調用父類的方法,實例域的聲明語句以及實例初始化語句塊存在位置關係,方法以構造方法作爲結束。
接下來回到這道題上來,啓動java虛擬機:
第一步:加載應用用到的類,加載順序爲Base–>Dervied,這裏涉及到方法的調用,但是當前類型中沒有static域或者是static{},所以方法不再討論。
第二步:對象實例化操作,首先我們new Dervied();,這樣就觸發對Dervied.的調用,但是根據以上知識(“並且子類的方法中會首先對父類方法的調用”)我們可以知道,Dervied.首先會調用到Base.方法,所以現在我們調用的方法是Base.,Base.方法中,首先是對name域(父類Base)進行初始化,然後調用tellName()方法,因爲子類以及覆寫了tellName()方法,所以tellName方法實際調用爲Dervied.tellName()方法,tellName方法要打印name域(子類Dervied)的值,但是當前Dervied對象中的name域還沒有被初始化,所以打印出來的值爲null。printName()方法執行與tellName()方法一致。
第三步:Base.方法返回到Dervied.方法中,緊接着對name域(子類Dervied)進行初始化操作,然後調用子類Dervied的tellName方法和printName方法,name域(子類Dervied)已經初始化完成,所以能正確打印出來。