這其實是去年校招時我遇到的一道阿里巴巴的筆試題(承認有點久遠了-。-),嗯,如果我沒記錯的話,當時是作為Java方向的一道選做大題。當然題意沒有這麼直白,題目只要求你寫出程式執行後所有System.out.println的輸出結果,其中程式是題目給的,而各個System.out.println的執行順序不同會導致最後程式輸出的結果也不同。
具體的題目我肯定記不清,不過我們可以換個直接的問法,如果類A和類B中有靜態變量,靜態語句塊,非靜態變量,非靜態語句塊,構造函數,靜態方法,非靜態方法,同時類A繼承類B,請問當實例化A時,類內部的加載順序是什麼?
當時我也是一頭霧水,事後我就自己寫了一個小Demo,這才知道了類內部的實際載入順,測試程式碼如下:
Class B:
public class B{ //静态变量 static int i=1; //静态语句块 static { System.out.println("Class B1:static blocks"+i); } //非静态变量 int j=1; //静态语句块 static{ i++; System.out.println("Class B2:static blocks"+i); } //构造函数 public B(){ i++; j++; System.out.println("constructor B: "+"i="+i+",j="+j); } //非静态语句块 { i++; j++; System.out.println("Class B:common blocks"+"i="+i+",j="+j); } //非静态方法 public void bDisplay(){ i++; System.out.println("Class B:static void bDisplay(): "+"i="+i+",j="+j); return ; } //静态方法 public static void bTest(){ i++; System.out.println("Class B:static void bTest(): "+"i="+i); return ; } }
Class A:
public class A extends B{ //静态变量 static int i=1; //静态语句块 static { System.out.println("Class A1:static blocks"+i); } //非静态变量 int j=1; //静态语句块 static{ i++; System.out.println("Class A2:static blocks"+i); } //构造函数 public A(){ super(); i++; j++; System.out.println("constructor A: "+"i="+i+",j="+j); } //非静态语句块 { i++; j++; System.out.println("Class A:common blocks"+"i="+i+",j="+j); } //非静态方法 public void aDisplay(){ i++; System.out.println("Class A:static void aDisplay(): "+"i="+i+",j="+j); return ; } //静态方法 public static void aTest(){ i++; System.out.println("Class A:static void aTest(): "+"i="+i); return ; } }
Class ClassLoading :
public class ClassLoading { public static void main (String args[]) { A a=new A(); a.aDisplay(); } }
程式運作結果如圖:
的看出java類別的整個載入過程。 1.若要載入類別A,則先載入執行其父類別B(Object)的靜態變數以及靜態語句區塊(執行先後順序按排列的先後順序)。2.然後再載入執行類別A的靜態變數以及靜態語句塊。 (且1、2步驟只會執行1次)
3.若需實例化類別A,則先呼叫其父類別B的建構子,並且在呼叫其父類別B的建構子前,依序先呼叫父類B中的非靜態變數及非靜態語句區塊.最後再呼叫父類別B中的建構子初始化。
4.然後再依序呼叫類別A中的非靜態變數及非靜態語句塊.最後呼叫A中的建構子初始化。 ( 並且3、4步驟可以重複執行)
5.而對於靜態方法和非靜態方法都是被動調用,即係統不會自動調用執行,所以用戶沒有調用時都不執行,主要區別在於靜態方法可以直接用類別名稱直接呼叫(實例化物件也可以),而非靜態方法只能先實例化物件後才能呼叫。