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

Java 類初始化順序詳解:靜態、實例與建構子的執行時機

     編輯:Java 相關知識

理解 Java 類初始化順序對於除錯繼承體系、解決循環依賴、掌握單例模式執行時機至關重要。本文結合程式碼範例與 JVM 載入機制,完整梳理父子類從載入到實例化的完整流程。

程式碼範例

public class TestInit {
    @Test
    public void test() {
        Child child = new Child();
    }
}

class Parent {
    public static String staticField = "父類靜態變數";
    public String instanceField = "父類實例變數";

    static { System.out.println(staticField); System.out.println("父類靜態塊"); }
    { System.out.println(instanceField); System.out.println("父類實例塊"); }
    public Parent() { System.out.println("父類建構子"); }
}

class Child extends Parent {
    public static String staticField = "子類靜態變數";
    public String instanceField = "子類實例變數";

    static { System.out.println(staticField); System.out.println("子類靜態塊"); }
    { System.out.println(instanceField); System.out.println("子類實例塊"); }
    public Child() { System.out.println("子類建構子"); }
}

執行輸出

父類靜態變數
父類靜態塊
子類靜態變數
子類靜態塊
父類實例變數
父類實例塊
父類建構子
子類實例變數
子類實例塊
子類建構子

五階段完整流程

  1. 類載入與連結: new Child 觸發類載入器尋找 Child.class,發現繼承 Parent,遞歸載入父類(無論是否建立父類實例)。
  2. 靜態初始化(自上而下): 依繼承層級依序執行:父類靜態變數賦值 → 父類靜態塊 → 子類靜態變數賦值 → 子類靜態塊。靜態成員只在類首次主動使用時初始化一次。
  3. 實例化準備: 所有相關類載入完成,開始在堆上分配 Child 物件記憶體。
  4. 父類實例初始化: 執行父類實例變數賦值 → 父類實例塊(非靜態塊) → 父類建構子主體。
    若建構子首行未顯式 super(...),隱式呼叫 super()。
  5. 子類實例初始化: 同樣順序:子類實例變數賦值 → 子類實例塊 → 子類建構子主體。

關鍵記憶口訣

靜態先行,父前子後;實例跟著建構走,父建構完再輪到子。

常見陷阱

  • 靜態塊中呼叫子類靜態方法: 子類靜態成員可能尚未初始化,導致取得預設值(null/0)。
  • 建構子中呼叫可覆寫方法: 父類建構子執行時,子類實例塊尚未跑,覆寫方法若依賴子類欄位會見到預設值。
  • 類變數前向引用: 靜態塊/變數宣告順序決定執行順序,不可在宣告前使用(編譯錯誤),但可在靜態塊中使用後宣告的靜態變數(僅賦值後可見)。

實戰建議

  1. 靜態資源(線程池、連線池、配置)放在靜態塊初始化,並捕獲異常避免 ExceptionInInitializerError。
  2. 實例欄位預設值盡量在宣告處給定,減少實例塊複雜度。
  3. 建構子保持簡潔,避免啟動執行緒、開啟連線等副作用操作;改用工廠方法或 init() 明確初始化。

掌握初始化順序,能讓您在設計繼承體系、排查啟動異常時更有底氣,寫出更可預測的 Java 程式碼。

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