這其實是去年校招時我遇到的一道阿里巴巴的筆試題(承認有點久遠了-。-),嗯,如果我沒記錯的話,當時是作為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.而對于靜態方法和非靜態方法都是被動調用,即系統不會自動調用執行,所以用戶沒有調用時都不執行,主要區別在于靜態方法可以直接用類名直接調用(實例化對象也可以),而非靜態方法只能先實例化對象后才能調用。
OK,今天就總結到這里了,如果有地方說的不好或有錯誤的地方,歡迎大家指出,定當改正,謝謝。