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

Java 初始化塊執行順序詳解:靜態塊 vs 非靜態塊 vs 構造器,含繼承鏈實戰驗證

     編輯:Java 程式設計入門

核心結論速覽

塊類型執行時機執行次數典型用途
靜態初始化塊 static { }類加載(Class.forName、首次 new、訪問靜態成員)每個類加載器中僅一次初始化靜態常量、註冊 JDBC 驅動、加載配置文件
非靜態初始化塊 { }每次實例化對象時,在構造器之前每 new 一次執行一次多構造器共享初始化邏輯、匿名內部類初始化
構造器非靜態塊之後每 new 一次實例字段賦值、業務邏輯

繼承鏈下的執行順序鐵律

父類 static 塊 → 子類 static 塊 → 父類 非靜態塊 → 父類構造器 → 子類 非靜態塊 → 子類構造器

口訣:靜態按層級從上到下,實例按層級從上到下,每層「塊先構造後」。

實戰驗證代碼

父類 Father

package freeblock;

public class Father {
    static { System.out.println("Father STATIC block"); }
    { System.out.println("Father instance block"); }
    public Father() { System.out.println("Father constructor"); }
}

子類 Son

package freeblock;

public class Son extends Father {
    static { System.out.println("Son STATIC block"); }
    { System.out.println("Son instance block"); }
    public Son() { System.out.println("Son constructor"); }
}

測試類 TestOrder

package freeblock;

public class TestOrder {
    public static void main(String[] args) throws Exception {
        System.out.println("========== 測試 1:Class.forName 載入 Father ==========");
        Class f = Class.forName("freeblock.Father");
        System.out.println("-------- Father 類已載入 --------");

        System.out.println("\n========== 測試 2:newInstance() 創建 Father 實例 ==========");
        f.newInstance();
        System.out.println("-------- Father 實例創建完成 --------");

        System.out.println("\n========== 測試 3:Class.forName 載入 Son ==========");
        Class s = Class.forName("freeblock.Son");
        System.out.println("-------- Son 類已載入 --------");

        System.out.println("\n========== 測試 4:newInstance() 創建 Son 實例 ==========");
        s.newInstance();
        System.out.println("-------- Son 實例創建完成 --------");
    }
}

執行輸出與解讀

========== 測試 1:Class.forName 載入 Father ==========
Father STATIC block
-------- Father 類已載入 --------

========== 測試 2:newInstance() 創建 Father 實例 ==========
Father instance block
Father constructor
-------- Father 實例創建完成 --------

========== 測試 3:Class.forName 載入 Son ==========
Son STATIC block
-------- Son 類已載入 --------

========== 測試 4:newInstance() 創建 Son 實例 ==========
Father instance block
Father constructor
Son instance block
Son constructor
-------- Son 實例創建完成 --------
  • 測試 1:只觸發 Father 靜態塊,Son 未載入故無輸出。
  • 測試 2:實例化 Father,依次執行「非靜態塊 → 構造器」。
  • 測試 3:載入 Son 類,JVM 先初始化父類靜態(已完成),再執行 Son 靜態塊。
  • 測試 4:實例化 Son,完美驗證繼承鏈順序:父塊→父構造→子塊→子構造。

進階知識點

1. Class.forName vs ClassLoader.loadClass

  • Class.forName("cn.Clazz") 默認 initialize=true,會執行靜態塊。
  • Class.forName("cn.Clazz", false, loader) 設置 initialize=false,僅載入不初始化,不執行靜態塊。
  • ClassLoader.loadClass("cn.Clazz") 內部調用 findClass,同樣不觸發初始化。

2. <clinit> 方法

Javac 編譯器將所有靜態字段賦值語句、靜態塊代碼按文順序合併生成 <clinit>()V 方法,由 JVM 在類初始化階段調用。此方法:

  • 隱式 static、final、synchronized(類級鎖)
  • 不可被直接調用、不可重寫
  • 父類 <clinit> 先於子類執行

3. 非靜態塊編譯為 <init> 方法

每個構造器開頭都會插入非靜態塊代碼,再執行構造器體。因此:

  • 多構造器共享非靜態塊邏輯
  • 匿名內部類無構造器,只能靠非靜態塊初始化

最佳實踐建議

  1. 靜態資源初始化:放入 static { },如資源包加載、線程池創建、單例預熱。
  2. 實例通用邏輯:多構造器類提取公共初始化到 { },避免代碼重複。
  3. 避免在初始化塊中拋受檢異常:靜態塊拋異常導致 ExceptionInInitializerError,類永久不可用;實例塊拋異常導致構造失敗,對象無法創建。
  4. 慎用反射繞過初始化:initialize=false 只用於極少數框架級場景(如代理、序列化),業務代碼禁用。

總結

掌握「靜態隨類載入一次,非靜態隨實例每次,繼承鏈父先子後、塊先構造後」這 24 字口訣,配合 <clinit>/<init> 底層機制理解,即可輕松應對面試、除錯、框架源碼閱讀中所有初始化順序問題。

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