Java 反射機制允許程式在執行期動態獲取類別資訊(屬性、方法、建構子)、建立實例、呼叫方法、甚至存取私有成員。這是框架開發(Spring、Hibernate、MyBatis)、動態代理、註解處理、熱部署等核心基礎。簡單說:編譯期不知道類別長什麼樣,執行期才決定怎麼用它。
| 核心類別 | 主要用途 |
|---|---|
Class<T> | 類別元資料入口,獲取屬性、方法、建構子、註解、父類別、介面等 |
Field | 成員變數(含靜態/實例、公有/私有) |
Method | 方法資訊與動態呼叫 |
Constructor<T> | 建構子資訊與動態實例化 |
Modifier | 修飾符解析(public、private、static、final 等) |
public Object getProperty(Object owner, String fieldName) throws Exception {
Class<?> ownerClass = owner.getClass();
Field field = ownerClass.getDeclaredField(fieldName); // 含私有
field.setAccessible(true); // 關鍵:繞過存取檢查
return field.get(owner);
}
關鍵點:getDeclaredField 可取得私有欄位,必須配合 setAccessible(true);若用 getField 僅能取得 public 欄位(含繼承)。
public Object getStaticProperty(String className, String fieldName) throws Exception {
Class<?> clazz = Class.forName(className);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(null); // 靜態欄位傳 null
}
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
Class<?> clazz = owner.getClass();
Class<?>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Method method = clazz.getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(owner, args);
}
注意:參數型別陣列必須精確匹配;基本型別對應包裝類別(int.class vs Integer.class)。
public Object invokeStaticMethod(String className, String methodName, Object[] args) throws Exception {
Class<?> clazz = Class.forName(className);
Class<?>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Method method = clazz.getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return method.invoke(null, args); // 靜態方法第一參數傳 null
}
public Object newInstance(String className, Object[] args) throws Exception {
Class<?> clazz = Class.forName(className);
Class<?>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
Constructor<?> ctor = clazz.getDeclaredConstructor(paramTypes);
ctor.setAccessible(true);
return ctor.newInstance(args);
}
// 無參建構子簡寫
Object obj = Class.forName("com.example.User").getDeclaredConstructor().newInstance();
public boolean isInstance(Object obj, Class<?> clazz) {
return clazz.isInstance(obj); // 等同於 obj instanceof clazz,但 clazz 可為變數
}
public Object getArrayElement(Object array, int index) {
return Array.get(array, index); // 統一處理基本型別與物件陣列
}
MethodHandle、VarHandle 或快取 Method/Field/Constructor 物件緩解。setAccessible(true) 破壞封裝,可能被惡意利用。模組化系統(JPMS)可限制反射存取;SecurityManager 亦可管控。module-info.java 加 opens 或啟動參數 --add-opens。Class、Method、Field、Constructor 物件,避免重複查詢。NoSuchMethodException、IllegalAccessException 等檢查型例外,給出明確錯誤提示。反射是 Java 動態能力的基石,賦予框架「不寫死類別」的彈性。掌握核心 API、理解效能代價、遵循模組化規範,才能在享受便利的同時守住系統穩定與安全。