// 完整流程:載入類 → 建立實例
Class> clazz = Class.forName("com.example.UserService");
UserService service = (UserService) clazz.getDeclaredConstructor().newInstance();
Class.forName() 觸發 JVM:載入 → 連結 → 初始化 三階段,執行類的 靜態程式碼塊 與 靜態欄位初始化。這與 new 關鍵字有本質區別。
| 維度 | new 關鍵字 | Class.forName() + newInstance() |
|---|---|---|
| 型別檢查 | 編譯期強型別 | 執行期弱型別(需轉型) |
| 建構子選擇 | 任意 public 建構子 | 僅無參建構子 |
| 類載入時機 | 首次主動使用(new、靜態存取等) | 呼叫 forName() 時強制載入 |
| 效能 | 較高(JIT 優化友善) | 較低(反射開銷、難以內聯) |
| 耦合度 | 緊耦合(硬編碼類名) | 鬆耦合(字串配置驅動) |
public class ServiceFactory {
public static Service createService(String type) {
if ("mysql".equals(type)) return new MySQLService();
else if ("oracle".equals(type)) return new OracleService();
throw new IllegalArgumentException("Unknown type");
}
}
// 工廠類
public class ServiceFactory {
public static Service createService() {
String className = readFromXmlConfig(); // "com.example.MySQLService"
Class> clazz = Class.forName(className);
return (Service) clazz.getDeclaredConstructor().newInstance();
}
}
優勢:新增實現類無需修改工廠程式碼,符合開閉原則。Spring、Dubbo、JDBC 驅動載入(Class.forName("com.mysql.jdbc.Driver"))均採用此模式。
// JDBC 4.0+ (JDK 6+) 已透過 SPI 自動載入,無需此行
// 但理解其原理至關重要:
Class.forName("com.mysql.cj.jdbc.Driver");
// 驅動類靜態塊中:DriverManager.registerDriver(new Driver());
// 之後即可:Connection conn = DriverManager.getConnection(url, user, pwd);
class DatabaseDriver {
static {
System.out.println("Driver 靜態塊執行 - 註冊到 DriverManager");
}
}
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("呼叫 forName 前");
Class.forName("DatabaseDriver"); // 輸出靜態塊內容
System.out.println("呼叫 forName 後");
Class.forName("DatabaseDriver"); // 不再輸出,類已初始化
}
}
結論:Class.forName() 保證類初始化(執行靜態塊)僅發生一次,後續呼叫直接返回快取的 Class 物件。
Class.newInstance() 自 JDK 9 標記過時(不處理受檢異常、僅支援 public 無參建構子)。現代寫法:
// 推薦:Constructor API,支援任意建構子、異常透傳
UserService service = clazz.getDeclaredConstructor(String.class, int.class)
.newInstance("config", 8080);
// 或:若確定無參建構子
UserService service = clazz.getDeclaredConstructor().newInstance();
Class.forName() 使用呼叫者的上下文類載入器,可能載入錯誤版本。指定載入器:Class.forName(name, true, classLoader)。module-info.java 宣告 opens package to java.base 或使用 --add-opens 參數,否則反射存取會被拒絕。MethodHandle、VarHandle 或程式碼生成 (ByteBuddy/ASM) 優化。Class.forName() 是 Java 動態性的基石,實現「配置驅動、介面導向、執行期決策」。理解其觸發類初始化、執行靜態塊、建立弱型別實例的完整語意,配合現代 Constructor API 替代過時 newInstance(),是掌握框架原理與編寫可擴展系統的必修課。