類的完整生命週期
加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載
本文聚焦前五階段(加載至初始化),即類加載過程。
1. 加載
- 獲取類的二進制字節流(來源不限:class 檔、JAR、網路、動態生成等)。
- 將靜態儲存結構轉化為方法區運行時資料結構。
- 在堆中生成 `java.lang.Class` 物件作為訪問入口。
- 開發者可控性最強:可自定義 `ClassLoader` 決定字節流來源。
2. 驗證
確保 Class 檔符合 JVM 規範、不危害安全。四大驗證:
- 檔案格式驗證(魔數、版本號、常數池結構等)
- 元數據驗證(語義分析:繼承關係、final 修飾符衝突等)
- 字節碼驗證(資料流/控制流分析,防止棧溢位、型別錯誤)
- 符號引用驗證(解析階段前驗證符號引用能否轉為直接引用)
3. 準備
- 為 類變數(static 欄位) 分配記憶體並設定零值(非使用者初始值)。
- 例外:`static final` 常數在編譯期即賦值,準備階段直接設定目標值。
4. 解析
將常數池中的符號引用轉換為直接引用(指標/偏移量)。
5. 初始化
- 執行類建構器 `()`:靜態變數賦值、靜態區塊執行。
- JVM 規範強制初始化的 4 情況(主動引用):
- `new` 實例化、讀寫 static 欄位(非 final)、呼叫 static 方法。
- 反射調用(`Class.forName`、`Method.invoke` 等)。
- 初始化子類前,先初始化父類。
- 啟動主類(含 `main` 方法)。
- 其餘(如訪問父類 static 欄位、定義陣列、讀取常數)屬被動引用,不觸發初始化。
總結:開發者可控制的兩個階段
| 階段 | 可控手段 |
| 加載 | 自定義 ClassLoader(決定字節流來源、熱部署、加密解密) |
| 初始化 | 編寫 static 區塊/靜態變數賦值邏輯 |
驗證、準備、解析完全由 JVM 主導。