JAVA面試題解惑系列(一)——類的初始化順序

大家在去參加面試的時候,經常會遇到這樣的考題:給你兩個類的代碼,它們之間是繼承的關係,每個類裏只有構造器方法和一些變量,構造器裏可能還有一段代碼對變量值進行了某種運算,另外還有一些將變量值輸出到控制檯的代碼,然後讓我們判斷輸出的結果。這實際上是在考查我們對於繼承情況下類的初始化順序的瞭解。

我們大家都知道,對於靜態變量、靜態初始化塊、變量、初始化塊、構造器,它們的初始化順序依次是(靜態變量、靜態初始化塊)>(變量、初始化塊)>構造器。我們也可以通過下面的測試代碼來驗證這一點:
Java代碼
public class InitialOrderTest {   
  
    // 靜態變量   
    public static String staticField = "靜態變量";   
    // 變量   
    public String field = "變量";   
  
    // 靜態初始化塊   
    static {   
        System.out.println(staticField);   
        System.out.println("靜態初始化塊");   
    }   
  
    // 初始化塊   
    {   
        System.out.println(field);   
        System.out.println("初始化塊");   
    }   
  
    // 構造器   
    public InitialOrderTest() {   
        System.out.println("構造器");   
    }   
  
    public static void main(String[] args) {   
        new InitialOrderTest();   
    }   
}  

運行以上代碼,我們會得到如下的輸出結果:

1、靜態變量
2、靜態初始化塊
3、變量
4、初始化塊
5、構造器

這與上文中說的完全符合。那麼對於繼承情況下又會怎樣呢?我們仍然以一段測試代碼來獲取最終結果:
Java代碼
class Parent {   
    // 靜態變量   
    public static String p_StaticField = "父類--靜態變量";   
    // 變量   
    public String p_Field = "父類--變量";   
  
    // 靜態初始化塊   
    static {   
        System.out.println(p_StaticField);   
        System.out.println("父類--靜態初始化塊");   
    }   
  
    // 初始化塊   
    {   
        System.out.println(p_Field);   
        System.out.println("父類--初始化塊");   
    }   
  
    // 構造器   
    public Parent() {   
        System.out.println("父類--構造器");   
    }   
}   
  
public class SubClass extends Parent {   
    // 靜態變量   
    public static String s_StaticField = "子類--靜態變量";   
    // 變量   
    public String s_Field = "子類--變量";   
    // 靜態初始化塊   
    static {   
        System.out.println(s_StaticField);   
        System.out.println("子類--靜態初始化塊");   
    }   
    // 初始化塊   
    {   
        System.out.println(s_Field);   
        System.out.println("子類--初始化塊");   
    }   
  
    // 構造器   
    public SubClass() {   
        System.out.println("子類--構造器");   
    }   
  
    // 程序入口   
    public static void main(String[] args) {   
        new SubClass();   
    }   
}  

運行一下上面的代碼,結果馬上呈現在我們的眼前:

1、父類--靜態變量
2、父類--靜態初始化塊
3、子類--靜態變量
4、子類--靜態初始化塊
5、父類--變量
6、父類--初始化塊
7、父類--構造器
8、子類--變量
9、子類--初始化塊
10、子類--構造器

現在,結果已經不言自明瞭。大家可能會注意到一點,那就是,並不是父類完全初始化完畢後才進行子類的初始化,實際上子類的靜態變量和靜態初始化塊的初始化是在父類的變量、初始化塊和構造器初始化之前就完成了。

那麼對於靜態變量和靜態初始化塊之間、變量和初始化塊之間的先後順序又是怎樣呢?是否靜態變量總是先於靜態初始化塊,變量總是先於初始化塊就被初始化了呢?實際上這取決於它們在類中出現的先後順序。我們以靜態變量和靜態初始化塊爲例來進行說明。

同樣,我們還是寫一個類來進行測試:
Java代碼
public class TestOrder {   
    // 靜態變量   
    public static TestA a = new TestA();   
      
    // 靜態初始化塊   
    static {   
        System.out.println("靜態初始化塊");   
    }   
      
    // 靜態變量   
    public static TestB b = new TestB();   
  
    public static void main(String[] args) {   
        new TestOrder();   
    }   
}   
  
class TestA {   
    public TestA() {   
        System.out.println("Test--A");   
    }   
}   
  
class TestB {   
    public TestB() {   
        System.out.println("Test--B");   
    }   
}  

運行上面的代碼,會得到如下的結果:

1、Test--A
2、靜態初始化塊
3、Test--B

大家可以隨意改變變量a、變量b以及靜態初始化塊的前後位置,就會發現輸出結果隨着它們在類中出現的前後順序而改變,這就說明靜態變量和靜態初始化塊是依照他們在類中的定義順序進行初始化的。同樣,變量和初始化塊也遵循這個規律。

瞭解了繼承情況下類的初始化順序之後,如何判斷最終輸出結果就迎刃而解了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章