反射創建物件是框架 (Spring/MyBatis/Jackson) 核心能力。本文系統講解 Class.newInstance() (已棄用) 與 Constructor.newInstance() 兩代 API、參數型別匹配、異常處理與封裝工具類。
// 方式 1: Class.newInstance() (Java 9 棄用, 僅能呼叫 public 無參建構子)
String s = (String) Class.forName("java.lang.String").newInstance();
// 方式 2: Constructor API (推薦, 統一入口)
Constructor c = String.class.getDeclaredConstructor(); // 獲取無參建構子
c.setAccessible(true); // 若為 private
String s2 = c.newInstance();
// 目標類
class User {
private String name; int age;
public User(String name, int age) { this.name=name; this.age=age; }
}
// 反射創建
Class> clazz = User.class;
// 1. 獲取建構子:參數型別按宣告順序
Constructor> cons = clazz.getDeclaredConstructor(String.class, int.class); // String, int (基本型別用 int.class)
cons.setAccessible(true); // 私有建構子必須
// 2. 實例化:參數值陣列順序、型別與建構子一致
User u = (User) cons.newInstance("張三", 25); // 自動拆箱 int → Integer
| Java 型別 | Class 物件獲取方式 | newInstance 實參 |
|---|---|---|
| 基本型別 int/long/boolean... | int.class / Integer.TYPE | 傳入基本值 25 (自動裝箱) |
| 包裝型別 Integer/String/自訂類 | Integer.class / String.class / MyClass.class | 傳入物件引用 |
| 陣列 | int[].class / String[].class | 傳入陣列實例 |
public final class ReflectUtils {
private static final Map, Constructor>> CONS_CACHE = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked")
public static T newInstance(Class clazz, Object... args) {
try {
Class>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Constructor> cons = CONS_CACHE.computeIfAbsent(clazz, c -> {
try {
Constructor> cn = c.getDeclaredConstructor(argTypes);
cn.setAccessible(true);
return cn;
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("No matching constructor for " + c + " with args " + Arrays.toString(argTypes), e);
}
});
return (T) cons.newInstance(args);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause()); // 拋出真實業務異常
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Reflective instantiation failed: " + clazz, e);
}
}
}
// 用法
User u = ReflectUtils.newInstance(User.class, "李四", 30);
OuterClass.this。clazz.getDeclaredConstructor(String.class, List.class) (泛型擦除後為原始型別)。Constructor 並開啟 setAccessible(true);極高效能場景改用 MethodHandle / ByteBuddy 生成字節碼。