上一節(jié)我們簡單總結(jié)了類的初始化順序。
父類(靜態(tài)變量、靜態(tài)初始化塊)>子類(靜態(tài)變量、靜態(tài)初始化塊)>
父類(變量、初始化塊)>父類構(gòu)造器>子類(變量、初始化塊)>子類構(gòu)造器。(變量和初始化塊按定義順序初始化)
構(gòu)造器調(diào)用的層次結(jié)構(gòu)帶來了一個有趣的兩難問題。如果在構(gòu)造器內(nèi)部調(diào)用正在構(gòu)造的對象的某個動態(tài)綁定方法,此時會出現(xiàn)什么情況呢?眾所周知,動態(tài)綁定的調(diào)用是在運行時才決定的,因為對象無法知道到底調(diào)用的是哪個類的方法。當我們在構(gòu)造器中調(diào)用動態(tài)綁定的方法,就會用到該方法被覆蓋之后的定義。因為被覆蓋的方法在對象被完全構(gòu)造之前就會被調(diào)用,因此這可能會導(dǎo)致一些難以發(fā)現(xiàn)的錯誤。
這是《Java編程思想》書內(nèi)的一段話,讀起來似乎比較晦澀難懂,我們直接來看看書上給出的代碼:
class Glyph{
void draw(){syste.out.println("Glyph.draw()");
Glyph(){
system.out.println("Glyph() before draw()");
draw();
system.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r){
radius = r;
system.out.println("RoundGlyph.RoundGLyph(), radius = " + radius);
}
void draw(){
system.out.println("RoundGlyph.draw(), radius = " + radius);
}
}
public class RolyConstructors {
public static void main(string[] args){
new RoundGlyph(5);
}
}
代碼運行結(jié)果
這個結(jié)果明顯已經(jīng)超出了我們的預(yù)期,細心的讀者就會察覺Java類初始化順序一文中得出的結(jié)論也許并不完整。但是這段代碼在邏輯方面也似乎沒什么錯誤,那為什么會這樣呢 ?
我們來分析一下代碼的運行過程
在main()方法內(nèi)只有一行代碼 new RoundGlyph(5);
1、首先訪問其父類 Glyph的構(gòu)造函數(shù)
輸出 System.out.println("Glyph() before draw()");
2、然后關(guān)鍵代碼來了,Glyph構(gòu)造函數(shù)內(nèi)調(diào)用了draw(),此方法在導(dǎo)出類中被覆蓋
此時調(diào)用的是被覆蓋后的draw()方法,即導(dǎo)出類RoundGlyph內(nèi)的draw()方法。此刻radius還沒有被賦初值,默認為0,因此輸出 RoundGlyph.draw(), radius = 0;
3、繼續(xù)執(zhí)行 System.out.println("Glyph() after draw()")
4、父類的構(gòu)造函數(shù)結(jié)束,執(zhí)行本類RoundGlyph的構(gòu)造函數(shù)
給radius賦值,radius = 5;
執(zhí)行 System.out.println("RoundGlyph.RoundGLyph(), radius = " + radius);
輸出 RoundGlyph.draw(), radius = 5 。
因此我們驚喜地得出了一個結(jié)論:
當我們在基類的構(gòu)造器內(nèi)調(diào)用了某個方法,并且該方法被導(dǎo)出類所覆蓋,此時調(diào)用的是導(dǎo)出類內(nèi)的方法而并非是基類本身擁有的方法。
編寫構(gòu)造器時有一條有效準則,用盡可能簡單的方法使對象進入正常狀態(tài),如果可以,避免調(diào)用其他方法。