反射是 Java 動態性的基石,框架(Spring、Hibernate、MyBatis)的核心實現均建立於此。本文從「什麼是反射」切入,系統講解 Class 對象獲取、動態實例化、結構探測(構造器、方法、字段、父類、接口)、私有成員訪問、數組操作等完整體系,並給出生產級代碼模板。
反射 = 運行時根據已實例化對象(或類名)還原類的完整元數據,並支持動態創建實例、調用方法、訪問字段。
關鍵點:Class 對象是反射的入口,每個類加載後在堆中生成唯一的 Class 實例,包含類的完整定義信息。
public class ClassObtainDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式一:對象.getClass() —— 最常用,已有實例時
String str = "hello";
Class> c1 = str.getClass();
// 方式二:類.class —— 編譯期已知類,無需實例,性能最優
Class c2 = String.class;
// 方式三:Class.forName("全限定名") —— 運行時動態決定類名,框架核心用法
Class> c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2 && c2 == c3); // true:同一類加載器下 Class 單例
}
}
public class InstantiateDemo {
public static void main(String[] args) throws Exception {
Class> clazz = Class.forName("java.lang.String");
// 現代方式:獲取無參構造器 -> 實例化
Constructor> noArgConstructor = clazz.getDeclaredConstructor();
String obj = (String) noArgConstructor.newInstance(); // ""
System.out.println("無參構造: 長度=" + obj.length());
}
}
// String(byte[] bytes) 構造器
Class> clazz = Class.forName("java.lang.String");
Constructor> constructor = clazz.getDeclaredConstructor(byte[].class);
byte[] bytes = {72, 101, 108, 108, 111}; // "Hello"
String obj = (String) constructor.newInstance((Object) bytes); // 注意:傳數組需轉 Object 避免展開
System.out.println("有參構造: " + obj); // Hello
Class> clazz = Boolean.class;
Constructor>[] constructors = clazz.getDeclaredConstructors(); // 所有聲明構造器(含private)
// Constructor>[] publicConstructors = clazz.getConstructors(); // 僅public
for (Constructor> c : constructors) {
System.out.println(c + " | 參數: " + Arrays.toString(c.getParameterTypes()));
}
Class> clazz = String.class;
// 獲取所有聲明方法(含private)
Method[] methods = clazz.getDeclaredMethods();
// 僅public方法(含繼承)
Method[] publicMethods = clazz.getMethods();
// 精確獲取並調用:public void print(int i)
Method printMethod = clazz.getDeclaredMethod("print", int.class); // 假設類有此方法
printMethod.invoke(targetObject, 10); // 實例方法傳對象
// 靜態方法調用:第一參數傳 null
Method staticMethod = clazz.getDeclaredMethod("staticMethod", String.class);
staticMethod.invoke(null, "args");
class Person {
public String name;
private int age;
public Person(String name, int age) { this.name = name; this.age = age; }
}
Person p = new Person("張三", 25);
Class> clazz = p.getClass();
// 讀取公有字段
Field nameField = clazz.getField("name");
System.out.println("姓名: " + nameField.get(p)); // 張三
// 讀寫私有字段:暴力反射
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 關鍵:繞過訪問檢查
System.out.println("年齡: " + ageField.get(p)); // 25
ageField.set(p, 26); // 修改
System.out.println("修改後: " + ageField.get(p)); // 26
Class> clazz = ArrayList.class;
// 父類
Class> superClass = clazz.getSuperclass(); // AbstractList
System.out.println("父類: " + superClass);
// 實現接口
Class>[] interfaces = clazz.getInterfaces(); // List, Collection, Iterable, Serializable, Cloneable, RandomAccess
System.out.println("接口: " + Arrays.toString(interfaces));
// 泛型父類/接口 (ParameterizedType)
Type genericSuper = clazz.getGenericSuperclass();
Type[] genericInterfaces = clazz.getGenericInterfaces();
int[] arr = {1, 2, 3, 4, 5};
Class> componentType = arr.getClass().getComponentType(); // int.class
System.out.println("元素類型: " + componentType.getName()); // int
// 通過 java.lang.Array 操作
int len = Array.getLength(arr); // 5
for (int i = 0; i < len; i++) {
System.out.print(Array.get(arr, i) + " "); // 1 2 3 4 5
}
// 修改元素
Array.set(arr, 0, 99);
System.out.println("\n修改後首元素: " + Array.get(arr, 0)); // 99
// 動態創建數組
Object newArr = Array.newInstance(String.class, 3); // String[3]
Array.set(newArr, 0, "A");
public class ReflectionFactory {
public static T createInstance(String className, Class type) {
try {
Class> clazz = Class.forName(className);
// 驗證類型兼容
if (!type.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("類型不匹配");
}
// 嘗試無參構造
return type.cast(clazz.getDeclaredConstructor().newInstance());
} catch (Exception e) {
throw new RuntimeException("反射創建失敗: " + className, e);
}
}
}
// 使用
UserService service = ReflectionFactory.createInstance("com.example.UserServiceImpl", UserService.class);
| 維度 | 影響 | 優化建議 |
|---|---|---|
| 性能 | 反射調用比直接調用慢 10-100倍 (JIT 優化後縮小) | 緩存 Class/Method/Field/Constructor 對象;高頻熱點用 MethodHandle/VarHandle |
| 安全 | 破壞封裝,可訪問 private | SecurityManager 限制 setAccessible;模塊化 (JPMS) 導出控制 |
| 異常 | 檢查異常多,代碼冗長 | 封裝工具類統一拋運行時異常 |
reflect-config.json,否則運行時報錯。反射賦予 Java 「運行時自我認知與操作」的能力。掌握 Class 獲取三式、Constructor/Method/Field 三大核心操作對象、暴力反射突破私有封裝、數組統一操作 API,並理解性能代價與安全邊界,是進階 Java 開發者與框架設計者的必修課。