我們有必要對整個初始化過程有所認識,其中包括繼承,對這個過程中發生的事情有一個整體性的概念。請觀察下述代碼:
//: Beetle.java // The full process of initialization. class Insect { int i = 9; int j; Insect() { prt("i = " + i + ", j = " + j); j = 39; } static int x1 = prt("static Insect.x1 initialized"); static int prt(String s) { System.out.println(s); return 47; } } public class Beetle extends Insect { int k = prt("Beetle.k initialized"); Beetle() { prt("k = " + k); prt("j = " + j); } static int x2 = prt("static Beetle.x2 initialized"); static int prt(String s) { System.out.println(s); return 63; } public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); } } ///:~
該程序的輸出如下:
static Insect.x initialized static Beetle.x initialized Beetle constructor i = 9, j = 0 Beetle.k initialized k = 63 j = 39
對Beetle運行Java時,發生的第一件事情是裝載程序到外面找到那個類。在裝載過程中,裝載程序注意它有一個基礎類(即extends關鍵字要表達的意思),所以隨之將其載入。無論是否准備生成那個基礎類的一個對象,這個過程都會發生(請試著將對象的創建代碼當作注釋標注出來,自己去證實)。
若基礎類含有另一個基礎類,則另一個基礎類隨即也會載入,以此類推。接下來,會在根基礎類(此時是Insect)執行static初始化,再在下一個衍生類執行,以此類推。保證這個順序是非常關鍵的,因為衍生類的初始化可能要依賴於對基礎類成員的正確初始化。
此時,必要的類已全部裝載完畢,所以能夠創建對象。首先,這個對象中的所有基本數據類型都會設成它們的默認值,而將對象句柄設為null。隨後會調用基礎類構建器。在這種情況下,調用是自動進行的。但也完全可以用super來自行指定構建器調用(就象在Beetle()構建器中的第一個操作一樣)。基礎類的構建采用與衍生類構建器完全相同的處理過程。基礎順構建器完成以後,實例變量會按本來的順序得以初始化。最後,執行構建器剩余的主體部分。