反射是 Java 動態性的基石,框架(Spring、Hibernate、MyBatis)底層皆大量依賴反射。本文系統講解 **獲取 Class 對象四種方式、類結構資訊擷取(欄位/方法/建構子/註解/泛型)、動態建立實例、動態調用方法、訪問私有成員、效能考量與安全限制**,附完整工具類範例。
Class> c1 = String.class; // 1. 類字面量 (編譯期已知)
Class> c2 = "hello".getClass(); // 2. 實例 getClass()
Class> c3 = Class.forName("java.lang.String"); // 3. 字串全限定名 (動態載入)
Class> c4 = int.class; // 4. 基本型別 .class
Class> c5 = Integer.TYPE; // 基本型別包裝類 .TYPE
public class ReflectionInspector {
public static void inspect(Class> clazz) {
System.out.println("=== 類基本資訊 ===");
System.out.println("完整名稱: " + clazz.getName());
System.out.println("簡單名稱: " + clazz.getSimpleName());
System.out.println("包名: " + clazz.getPackageName());
System.out.println("修飾符: " + Modifier.toString(clazz.getModifiers()));
System.out.println("父類: " + (clazz.getSuperclass() != null ? clazz.getSuperclass().getName() : "無"));
System.out.println("介面: " + Arrays.toString(clazz.getInterfaces()));
System.out.println("\n=== 建構子 ===");
for (Constructor> ctor : clazz.getDeclaredConstructors()) {
System.out.println(Modifier.toString(ctor.getModifiers()) + " " + ctor.getName() + Arrays.toString(ctor.getParameterTypes()));
}
System.out.println("\n=== 成員欄位 (含私有) ===");
for (Field f : clazz.getDeclaredFields()) {
System.out.println(Modifier.toString(f.getModifiers()) + " " + f.getType().getSimpleName() + " " + f.getName());
}
System.out.println("\n=== 成員方法 (含私有) ===");
for (Method m : clazz.getDeclaredMethods()) {
System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName() + Arrays.toString(m.getParameterTypes()));
}
System.out.println("\n=== 註解 ===");
for (Annotation ann : clazz.getDeclaredAnnotations()) {
System.out.println(ann);
}
}
}
Constructor> ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // 繞過 private
Object obj = ctor.newInstance("Tom", 18);
Method method = clazz.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true);
Object result = method.invoke(obj, "World"); // 非 static 傳實例
Method staticMethod = clazz.getDeclaredMethod("staticHelper", int.class);
staticMethod.setAccessible(true);
Object result = staticMethod.invoke(null, 100); // static 傳 null
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(obj, "Jerry"); // 寫入
String val = (String) field.get(obj); // 讀取
Java 泛型擦除後僅保留原始類型,但**方法簽名、欄位宣告、父類泛型參數**的泛型資訊保留在 Class 中,可通過 Type 系統擷取:
public class GenericResolver {
// 解析欄位實際泛型類型
public static Class> getFieldGenericType(Field field) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type actualType = pt.getActualTypeArguments()[0];
return (Class>) actualType;
}
return (Class>) type;
}
// 解析方法回傳值泛型
public static Class> getMethodReturnGenericType(Method method) {
Type type = method.getGenericReturnType();
if (type instanceof ParameterizedType) {
return (Class>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
return (Class>) type;
}
}
| 面向 | 說明 | 對策 |
|---|---|---|
| 效能 | 反射調用比直接調用慢 10~100 倍 (JIT 難優化、存取檢查、裝箱/拆箱) | 緩存 Class/Method/Field 對象;熱點路徑用 MethodHandle/VarHandle (Java 9+) 或代碼生成 (ASM/ByteBuddy) |
| 安全 | setAccessible(true) 可能被 SecurityManager 禁止 (模組化後更嚴格) | 模組系統中需在 module-info.java opens 對應包;或啟動參數 --add-opens |
| 封裝破壞 | 可隨意讀寫 private 成員 | 僅在框架/測試/序列化等必要場景使用,業務代碼禁用 |
public final class ReflectUtils {
public static <T> T newInstance(Class<T> clazz, Object... args) {
try {
Constructor<T> ctor = findConstructor(clazz, args);
ctor.setAccessible(true);
return ctor.newInstance(args);
} catch (Exception e) { throw new RuntimeException(e); }
}
public static Object invokeMethod(Object target, String methodName, Object... args) {
try {
Method method = findMethod(target.getClass(), methodName, args);
method.setAccessible(true);
return method.invoke(target, args);
} catch (Exception e) { throw new RuntimeException(e); }
}
public static void setField(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(target, value);
} catch (Exception e) { throw new RuntimeException(e); }
}
public static Object getField(Object target, String fieldName) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(target);
} catch (Exception e) { throw new RuntimeException(e); }
}
private static Constructor> findConstructor(Class> clazz, Object[] args) {
for (Constructor> ctor : clazz.getDeclaredConstructors()) {
if (matches(ctor.getParameterTypes(), args)) return ctor;
}
throw new NoSuchMethodException("No matching constructor");
}
private static Method findMethod(Class> clazz, String name, Object[] args) {
for (Method m : clazz.getDeclaredMethods()) {
if (m.getName().equals(name) && matches(m.getParameterTypes(), args)) return m;
}
throw new NoSuchMethodException("No matching method: " + name);
}
private static boolean matches(Class>[] paramTypes, Object[] args) {
if (paramTypes.length != args.length) return false;
for (int i = 0; i < paramTypes.length; i++) {
if (args[i] != null && !paramTypes[i].isAssignableFrom(args[i].getClass())) return false;
}
return true;
}
}
掌握反射核心 API 與泛型擷取技巧,是閱讀框架源碼、構建通用組件的必備能力。