首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Java 構造器初始化順序深度解析:靜態塊、實例塊、構造器、父類、成員變量誰先誰後?

     編輯:關於C語言

經典面試代碼


class Window {
    Window(int marker) { System.out.println("Window(" + marker + ")"); }
}

class House {
    Window w1 = new Window(1);  // 成員變量初始化
    House() {                   // 構造器
        System.out.println("House()");
        w3 = new Window(33);    // 構造器內賦值
    }
    Window w2 = new Window(2);  // 成員變量初始化
    void f() { System.out.println("f()"); }
    Window w3 = new Window(3);  // 成員變量初始化
}

public class OrderOfInitialization {
    public static void main(String[] args) {
        House h = new House();
        h.f();
    }
}

輸出結果


Window(1)
Window(2)
Window(3)
House()
Window(33)
f()

初始化順序鐵律 (單類)

  1. 靜態變量/靜態塊:按代碼順序,類加載時執行 (僅一次)。
  2. 實例變量/實例初始化塊:按代碼順序,在構造器之前。
  3. 構造器:最後執行。

關鍵點:w1, w2, w3 的 new Window(...) 屬於實例變量初始化,所以在 House() 構造器體之前全部完成,無論它們在代碼中出現在構造器前還是後。

繼承鏈初始化順序 (完整版)


父類靜態變量/塊 → 子類靜態變量/塊
→ 父類實例變量/塊 → 父類構造器
→ 子類實例變量/塊 → 子類構造器

記憶口訣:靜態先行,父前子後,塊前構後。

驗證實驗:移動 w1 到構造器後


class House {
    House() {
        System.out.println("House()");
        w3 = new Window(33);
    }
    Window w1 = new Window(1); // 移到構造器後面
    Window w2 = new Window(2);
    Window w3 = new Window(3);
}

輸出完全相同:


Window(1)
Window(2)
Window(3)
House()
Window(33)
f()

證明:實例變量初始化與代碼位置無關,只看聲明順序,且在構造器之前。

再加一個 Door 類驗證


class Door { Door() { System.out.println("Door()"); } }

class House {
    House() { System.out.println("House()"); w3 = new Window(33); }
    Window w1 = new Window(1);
    Window w2 = new Window(2);
    Window w3 = new Window(3);
    Door door = new Door(); // 新增成員
}

輸出:


Window(1)
Window(2)
Window(3)
Door()
House()
Window(33)
f()

Door() 在 House() 之前,再次確認:所有成員變量 (含不同類型) 初始化先於構造器體。

常見誤區

誤區真相
構造器先執行構造器最後執行
代碼寫在構造器前的先初始化按聲明順序,與構造器位置無關
w3 = new Window(33) 是初始化是賦值,發生在構造器體內,晚於初始化
靜態塊每次 new 都執行類加載時僅執行一次

最佳實踐建議

  1. 成員變量聲明處直接初始化:private Window w = new Window(1); 清晰可見。
  2. 避免在構造器中做複雜初始化:拆分為私有 init() 方法,或用構建器模式。
  3. 小心繼承中的初始化順序:父類構造器調用可覆蓋子類實例變量初始化的值。
  4. final 變量:必須在聲明處、實例塊或構造器中初始化,編譯器強制檢查。

字節碼視角 (javap -c)


// House 構造器字節碼片段
0: aload_0
1: invokespecial #1  // Object.
4: aload_0
5: new #2          // Window
8: dup
9: iconst_1
10: invokespecial #3 // Window.(int)
13: putfield #4    // w1
16: aload_0
17: new #2
20: dup
21: iconst_2
22: invokespecial #3
25: putfield #5    // w2
28: aload_0
29: new #2
32: dup
33: iconst_3
34: invokespecial #3
37: putfield #6    // w3
40: aload_0
41: new #7         // Door
44: dup
45: invokespecial #8
48: putfield #9    // door
// 以上為實例初始化 (putfield)
// 以下為構造器體
51: getstatic #10  // System.out
54: ldc #11        // "House()"
56: invokevirtual #12
59: aload_0
60: new #2
63: dup
64: bipush 33
66: invokespecial #3
69: putfield #6    // w3 = new Window(33)
72: return

字節碼直觀展示:invokespecial Object. → 所有 putfield (成員初始化) → 構造器體指令。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved