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

Java 程序初始化順序完全圖解:靜態區塊、實例區塊、建構子誰先跑?

     編輯:Java 相關知識

為什麼要搞清初始化順序?

《Core Java》等經典教材常只講建構子鏈,卻忽略「靜態區塊、實例區塊、建構子、繼承鏈」交織的完整順序。實戰中初始化順序錯誤會導致難以復現的 Bug。本文結合位元碼視角,梳理出一套「不背誦、能推導」的完整規則。

統一初始化順序口訣

  1. 父類靜態(靜態欄位、靜態區塊,按代碼順序)
  2. 子類靜態(同上)
  3. 父類實例(實例欄位、實例區塊,按代碼順序)
  4. 父類建構子
  5. 子類實例(實例欄位、實例區塊,按代碼順序)
  6. 子類建構子

核心原則:<clinit>()(類初始化)先於 <init>()(對象初始化);父類先於子類;靜態先於實例;代碼順序決定同層級順序。

實戰演練:複雜繼承鏈初始化

class FirstClass {
    FirstClass(int i) { System.out.println("FirstClass(" + i + ")"); }
    void useMethod(int k) { System.out.println("useMethod(" + k + ")"); }
}

class SecondClass {
    static FirstClass fc1 = new FirstClass(1);  // ① 靜態欄位
    FirstClass fc3 = new FirstClass(3);         // ② 實例欄位
    static {                                     // ③ 靜態區塊
        FirstClass fc2 = new FirstClass(2);
    }
    {                                            // ④ 實例區塊
        System.out.println("SecondClass's instance block");
    }
    SecondClass() {                              // ⑤ 建構子
        System.out.println("SecondClass()");
    }
    FirstClass fc4 = new FirstClass(4);         // ⑥ 實例欄位
}

public class InitiationDemo {
    SecondClass sc1 = new SecondClass();        // ⑦ 實例欄位
    {                                            // ⑧ 實例區塊
        System.out.println("Hello Java World!");
    }
    public static void main(String[] args) {    // ⑨ main 入口
        System.out.println("Inside main()");
        SecondClass.fc1.useMethod(100);         // ⑩ 訪問靜態欄位
        InitiationDemo idObj = new InitiationDemo(); // ⑪ new 本類
    }
    static SecondClass sc2 = new SecondClass(); // ⑫ 靜態欄位
}

執行輸出與逐步解析

FirstClass(1)      ← ⑫ 觸發 SecondClass 類初始化 → ①
FirstClass(2)      ← ③ 靜態區塊
FirstClass(3)      ← ⑪ new SecondClass() → 父類無靜態 → 子類實例欄位 ②
SecondClass's instance block  ← ④ 實例區塊
FirstClass(4)      ← ⑥ 實例欄位
SecondClass()      ← ⑤ 建構子
Inside main()      ← ⑨ main 開始
useMethod(100)     ← ⑩
FirstClass(3)      ← ⑪ new InitiationDemo() → new SecondClass() 再走一遍實例初始化
SecondClass's instance block
FirstClass(4)
SecondClass()
Hello Java World!  ← ⑧ 實例區塊

關鍵節點詳細拆解

1. 類初始化觸發點:static SecondClass sc2 = new SecondClass();

  • JVM 啟動 → 執行 InitiationDemo 的 <clinit>()
  • 按代碼順序:先執行 ⑦?錯! ⑦ 是實例欄位,屬於 <init>()
  • 先執行 ⑫ static SecondClass sc2 → 觸發 SecondClass 的 <clinit>()
  • SecondClass 無父類靜態 → 直接執行 ① ③

2. main 方法執行時,類已初始化完畢

  • SecondClass.fc1.useMethod(100) 直接使用已初始化好的靜態欄位,不再觸發初始化

3. new InitiationDemo() 觸發本類 <init>()

  • 父類 Object <init>()
  • 按代碼順序:⑦ new SecondClass() → 完整走一遍 SecondClass 的實例初始化(②④⑥⑤)
  • ⑧ 實例區塊 "Hello Java World!"
  • 本類無顯式建構子,編譯器生成無參 <init>() 結束

常見誤區與避坑指南

誤區真相
「靜態區塊在 main 之前執行」靜態區塊在「類首次主動使用」時執行,不一定是 main 前
「實例區塊比建構子早」實例區塊與實例欄位按代碼順序交織,統一在 super() 之後、建構子本體之前
「父類建構子執行前,子類實例欄位已初始化」父類 <init>() 完全結束後,才輪到子類實例欄位/區塊
「this() 可寫在建構子任意位置」this(...) 必須是建構子第一行,否則編譯錯誤

位元碼視角一鍵驗證

javap -c -v YourClass.class

查看 <clinit>() 與 <init>() 的指令序列,順序一目了然。

總結

初始化順序 = 類載入時機 + 父子層級 + 靜態優先 + 代碼順序。掌握這四個維度,任何複雜繼承鏈初始化都能手動推導,無需死記硬背。

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