| 塊類型 | 執行時機 | 執行次數 | 典型用途 |
|---|---|---|---|
靜態初始化塊 static { } | 類加載(Class.forName、首次 new、訪問靜態成員) | 每個類加載器中僅一次 | 初始化靜態常量、註冊 JDBC 驅動、加載配置文件 |
非靜態初始化塊 { } | 每次實例化對象時,在構造器之前 | 每 new 一次執行一次 | 多構造器共享初始化邏輯、匿名內部類初始化 |
| 構造器 | 非靜態塊之後 | 每 new 一次 | 實例字段賦值、業務邏輯 |
父類 static 塊 → 子類 static 塊 → 父類 非靜態塊 → 父類構造器 → 子類 非靜態塊 → 子類構造器
口訣:靜態按層級從上到下,實例按層級從上到下,每層「塊先構造後」。
Fatherpackage freeblock;
public class Father {
static { System.out.println("Father STATIC block"); }
{ System.out.println("Father instance block"); }
public Father() { System.out.println("Father constructor"); }
}
Sonpackage 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"); }
}
TestOrderpackage 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 實例創建完成 --------
Class.forName vs ClassLoader.loadClassClass.forName("cn.Clazz") 默認 initialize=true,會執行靜態塊。Class.forName("cn.Clazz", false, loader) 設置 initialize=false,僅載入不初始化,不執行靜態塊。ClassLoader.loadClass("cn.Clazz") 內部調用 findClass,同樣不觸發初始化。<clinit> 方法Javac 編譯器將所有靜態字段賦值語句、靜態塊代碼按文順序合併生成 <clinit>()V 方法,由 JVM 在類初始化階段調用。此方法:
static、final、synchronized(類級鎖)<clinit> 先於子類執行<init> 方法每個構造器開頭都會插入非靜態塊代碼,再執行構造器體。因此:
static { },如資源包加載、線程池創建、單例預熱。{ },避免代碼重複。ExceptionInInitializerError,類永久不可用;實例塊拋異常導致構造失敗,對象無法創建。initialize=false 只用於極少數框架級場景(如代理、序列化),業務代碼禁用。掌握「靜態隨類載入一次,非靜態隨實例每次,繼承鏈父先子後、塊先構造後」這 24 字口訣,配合 <clinit>/<init> 底層機制理解,即可輕松應對面試、除錯、框架源碼閱讀中所有初始化順序問題。