Java 中五種代碼塊 (靜態、構造/實例、普通、同步、構造函數) 構成了類初始化與對象創建的完整生命週期。掌握它們的**執行時機、順序、作用域、線程安全語義**,是寫出正確、高效 Java 代碼的基礎。
| 類型 | 語法 | 執行時機 | 執行次數 | 典型用途 | 訪問權限 |
|---|---|---|---|---|---|
| 靜態代碼塊 | static { ... } | 類加載階段 (ClassLoader 加載 .class 文件時) | 一次 (每類加載一次) | 類級別初始化:靜態集合填充、單例預創建、驅動註冊、JNI 加載 | 僅訪問靜態成員 |
| 構造代碼塊 (實例初始化塊) | { ... } | 對象創建時,**構造函數之前** | 每 new 一次執行一次 | 多構造函數共享的初始化邏輯 (DRY)、匿名內部類實例化配置 | 訪問所有成員 (含 this) |
| 構造函數 | ClassName() { ... } | 對象創建時,構造代碼塊之後 | 每 new 一次執行一次 | 對象個性化初始化、參數校驗、依賴注入 | 訪問所有成員 |
| 普通代碼塊 | 方法內 { ... } | 方法執行到該塊時 | 每次方法調用執行 | 限制變量作用域、臨時邏輯分組、舊版 Java 無 try-with-resources 時資源管理 | 方法局部變量 |
| 同步代碼塊 | synchronized(obj) { ... } | 線程進入該塊時 | 每次線程進入執行 | 細粒度線程同步、保護共享資源臨界區 | 鎖對象可訪問的成員 |
父類靜態字段賦值 → 父類靜態代碼塊 (按聲明順序)
→ 子類靜態字段賦值 → 子類靜態代碼塊 (按聲明順序)
→ main 方法入口 (若為啟動類)
關鍵: 靜態代碼塊**優於 main 方法**、**優於任何構造代碼塊/構造函數**執行。全 JVM 生命週期**僅執行一次** (除非類被卸載重載)。
父類構造代碼塊 → 父類構造函數
→ 子類構造代碼塊 → 子類構造函數
關鍵: 構造代碼塊**優於構造函數**執行。每次 new 都會按順序執行。**構造代碼塊會被複製到每個構造函數開頭** (字節碼層面)。
public class BlockOrderDemo {
// 1. 靜態字段
static String staticField = initStaticField();
static { System.out.println("【父類】靜態代碼塊"); }
static String initStaticField() { System.out.println("【父類】靜態字段賦值"); return "static"; }
// 2. 構造代碼塊
{ System.out.println("【父類】構造代碼塊"); }
// 3. 構造函數
public BlockOrderDemo() { System.out.println("【父類】構造函數"); }
public static void main(String[] args) {
System.out.println("=== main 方法開始 ===");
new BlockOrderDemo();
System.out.println("--- 分割線 ---");
new BlockOrderDemo();
}
}
/* 輸出:
【父類】靜態字段賦值
【父類】靜態代碼塊
=== main 方法開始 ===
【父類】構造代碼塊
【父類】構造函數
--- 分割線 ---
【父類】構造代碼塊
【父類】構造函數
*/
// 1. 同步實例方法 等價於 synchronized(this) { 方法體 }
public synchronized void method() { ... }
// 2. 同步代碼塊 - 實例鎖
public void method() {
synchronized(this) { ... } // 鎖當前對象
}
// 3. 同步代碼塊 - 顯式鎖對象 (推薦,封鎖內部狀態)
private final Object lock = new Object(); // 或 private final byte[] lock = new byte[0];
public void method() {
synchronized(lock) { ... }
}
// 4. 同步靜態方法 / 類鎖
public static synchronized void staticMethod() { ... }
// 等價於
public static void staticMethod() {
synchronized(MyClass.class) { ... }
}
synchronized(this) 公開鎖)。lockForCache, lockForDB),提高並發度。new byte[0] 創建最小對象 (無數據載荷),節省內存,語義純粹作為鎖。public class Singleton {
// 1. volatile 禁止指令重排,保證初始化可見性
private static volatile Singleton instance;
// 2. 私有構造函數
private Singleton() {
// 構造代碼塊邏輯可放這裡
System.out.println("Singleton 構造函數執行");
}
// 3. 靜態工廠方法
public static Singleton getInstance() {
// 第一次檢查 (無鎖,性能)
if (instance == null) {
// 4. 同步代碼塊 - 類鎖 (或專用靜態鎖對象)
synchronized(Singleton.class) {
// 第二次檢查 (防止多線程並發創建)
if (instance == null) {
instance = new Singleton(); // 構造函數 + 構造代碼塊
}
}
}
return instance;
}
}
| 誤區 | 真相 |
|---|---|
| 構造代碼塊在構造函數之後執行 | 構造代碼塊優於構造函數,字節碼層面被複製到構造函數開頭 |
| 靜態代碼塊每次 new 都執行 | 類加載時僅執行一次,與對象創建無關 |
| 普通代碼塊定義局部變量可在塊外訪問 | 塊內變量作用域僅限塊內,編譯錯誤 |
| synchronized(this) 和 synchronized(lock) 效果完全相同 | 鎖對象不同,保護範圍不同;this 公開,lock 封裝 |
| 靜態代碼塊可訪問 this / 非靜態字段 | 靜態上下文無 this,僅能訪問靜態成員 |
| 構造函數中調用同步方法安全 | 構造期間對象未發布,無需同步;若發布 this (如註冊監聽器) 則有洩漏風險 |
需要初始化邏輯?
├─ 類級別、全局唯一、一次性? → static { } 靜態代碼塊
├─ 對象級別、每次 new 都執行、多構造函數共享? → { } 構造代碼塊
├─ 對象級別、個性化參數、依賴注入? → Constructor 構造函數
├─ 方法內臨時邏輯、限制變量作用域? → { } 普通代碼塊
└─ 多線程保護共享資源、細粒度鎖? → synchronized(lock) { } 同步代碼塊
理解代碼塊的生命週期邊界、執行順序鐵律、字節碼級實現機制,能幫助你在初始化邏輯重構、單例實現、線程同步優化、匿名內部類使用等場景中做出最優選擇,避免「靜態塊拋異常導致類加載失敗」「構造塊重複代碼」「鎖粒度過粗」等典型工程問題。