1. 程式人生 > >Java——物件初始化順序

Java——物件初始化順序

一、 程式碼塊的概念

在探究物件初始化順序之前,我們先通過程式碼來了解一下程式碼塊的概念。

class  Test{
    public static String str1;  //靜態欄位

    public String str2;         //普通欄位

    static{                     
        //靜態程式碼塊
    }

    {
        //構造程式碼塊
    }

    public Test() {  
        //建構函式
    }
}

二、 建立子類物件時,物件的初始化順序

1. 欄位初始化、程式碼塊和建構函式的執行順序

我們先看程式碼和結果

public class CodeBlockTest {

    public static void main(String[] args) {
        Child child = new Child();
    }

}

class Father {
    public static String fatherStr1 = "fatherStr1(靜態欄位初始化值)";

    public String fatherStr2 = "fatherStr2(欄位初始化值)";

    static {
        System.out.println("父類靜態程式碼塊:"
+ fatherStr1); fatherStr1 = "fatherStr1(靜態程式碼塊賦值)"; } { System.out.println("父類構造程式碼塊:" + fatherStr2); fatherStr2 = "fatherStr2(構造程式碼塊賦值)"; } public Father() { System.out.println("父類建構函式塊:" + fatherStr2); fatherStr2 = "fatherStr2(建構函式賦值)"; } } class Child extends Father { public
static String childStr1 = "childStr1(靜態欄位初始化值)"; public String childStr2 = "childStr2(欄位初始化值)"; static { System.out.println("子類靜態程式碼塊:" + childStr1); childStr1 = "childStr1(靜態程式碼塊賦值)"; } { System.out.println("子類構造程式碼塊:" + childStr2); childStr2 = "childStr2(構造程式碼塊賦值)"; } public Child() { System.out.println("子類建構函式:" + childStr2); childStr2 = "childStr2(建構函式賦值)"; } } // 輸出結果: // 父類靜態程式碼塊:fatherStr1(靜態欄位初始化值) // 子類靜態程式碼塊:childStr1(靜態欄位初始化值) // 父類構造程式碼塊:fatherStr2(欄位初始化值) // 父類建構函式塊:fatherStr2(構造程式碼塊賦值) // 子類構造程式碼塊:childStr2(欄位初始化值) // 子類建構函式:childStr2(構造程式碼塊賦值)

通過每執行一個程式碼塊或建構函式,輸出欄位在上一程式碼塊執行後的值,以此來探究物件的初始化順序。
由目前的輸出結果可知,對於物件的初始化順序,我們可以得出以下結論:
1. 父類靜態欄位初始化
2. 父類靜態程式碼塊、子類靜態欄位初始化 (接下來探究兩者的順序)
3. 子類靜態程式碼塊
4. 父類普通欄位初始化
5. 父類構造程式碼塊
6. 父類建構函式
7. 子類普通欄位初始化
8. 子類構造程式碼塊
9. 子類建構函式

2. 父類靜態程式碼塊和子類靜態欄位初始化的執行順序

還是一樣,我們通過程式碼的執行結果來探究兩者間的執行順序。

public class CodeBloacTest2 {

    public static void main(String[] args) {
        Child child = new Child();
    }

}


class Father {
    public static String fatherStr = "(靜態欄位初始化值)";

    static {
        System.out.println("父類靜態程式碼塊:fatherStr" + fatherStr);
        fatherStr = "(靜態程式碼塊賦值)";
    }
}

class Child extends Father {

    public static String childStr = fatherStr;

    static {
        System.out.println("子類靜態程式碼塊:childStr = fatherStr" + childStr);
        childStr = "(靜態程式碼塊賦值)";
    }
}

//    輸出結果:
//    父類靜態程式碼塊:fatherStr(靜態欄位初始化值)
//    子類靜態程式碼塊:childStr = fatherStr(靜態程式碼塊賦值)

我們在子類靜態欄位childStr初始化的時候,賦的是父類靜態欄位fatherStr的值。由輸出結果可知,childStr初始化後的值是父類靜態程式碼塊執行後賦予fatherStr的值。由此可知兩者的執行順序為:父類靜態程式碼塊==>子類靜態欄位初始化

三、 結論

  1. 父類靜態欄位初始化
  2. 父類靜態程式碼塊
  3. 子類靜態欄位初始化
  4. 子類靜態程式碼塊
  5. 父類普通欄位初始化
  6. 父類構造程式碼塊
  7. 父類建構函式
  8. 子類普通欄位初始化
  9. 子類構造程式碼塊
  10. 子類建構函式

通過結論我們可以很明顯的看出:static欄位、程式碼塊的執行順序優先於非static欄位、程式碼塊。這是因為在靜態域是屬於類的,在類載入後就一直存在;而普通域需要建立物件才能訪問。而在建立物件時,需要先載入父類,然後再載入子類,因此父類的靜態欄位初始化和靜態程式碼塊執行先於子類