遇到`Class.forName("A")`不拋`ClassNotFoundException`、靜態塊不執行、輸出`class A`但帶包名的詭異現象?這通常是類加載器命名空間隔離或類已被加載但未初始化導致。
現象還原與核心原因
- 現象:`forName("A")`返回Class對象,但靜態塊未執行;換成`Employee`則拋異常。
- 原因:類`A`可能已被當前類加載器加載但未初始化(僅觸發了加載階段),或存在同名類在不同加載器中(如Tomcat WebappClassLoader vs CommonClassLoader)。
- 關鍵點:`Class.forName(String name)`默認會初始化類(執行靜態塊)。若未執行,說明調用時類已處於已初始化狀態,或傳入的`name`非完全限定名導致加載了其他同名類。
排查步驟
- 檢查完全限定名:確保傳入`com.pkg.A`而非簡單類名`A`。
- 驗證類加載器:打印`Class.forName("A").getClassLoader()`對比預期加載器。
- 檢查靜態塊執行條件:靜態塊僅在首次主動使用類時執行(new、訪問靜態字段/方法、反射調用、子類初始化)。若之前已觸發,再次`forName`不會重複執行。
- 排除同名類衝突:項目依賴、應用服務器環境下易出現多版本同名類。
代碼驗證模板
try {
Class> clazz = Class.forName("com.pkg.A", true, Thread.currentThread().getContextClassLoader());
// 第二參數true=初始化,第三參數指定加載器
System.out.println("ClassLoader: " + clazz.getClassLoader());
System.out.println("CanonicalName: " + clazz.getCanonicalName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
最佳實踐
- 始終使用完全限定類名。
- 明確指定類加載器(`Thread.currentThread().getContextClassLoader()`或目標類的`.class.getClassLoader()`)。
- 理解類生命周期:加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載。
搞清類加載機制,是排查Java疑難雜症、熱部署、模塊化開發的基石。