Java 反射允許運行時檢查類結構、動態創建對象、調用方法/訪問字段、加載未知類。它是 Spring、Hibernate、MyBatis、Gson、JUnit 等框架的基石。核心類位於 java.lang.reflect 包。
Class<?> 對象Constructor、Method、FieldnewInstance()、invoke()、get()/set() 執行操作
Class> clazz1 = String.class; // 1. 類字面量 (編譯期已知,不初始化類)
Class> clazz2 = "hello".getClass(); // 2. 實例 getClass() (運行期獲取實際類型)
Class> clazz3 = Class.forName("java.lang.String"); // 3. 字符串名稱 (會初始化類,拋 ClassNotFoundException)
import java.lang.reflect.Method;
public class ReflectionInvokeDemo {
public static void main(String[] args) throws Exception {
// 目標對象
Printer printer = new Printer();
// 1. 獲取 Class
Class> clazz = printer.getClass(); // 或 Printer.class
// 2. 獲取 Method (方法名 + 參數類型)
Method printMethod = clazz.getMethod("print", String.class);
// 3. 調用 (對象實例 + 實參)
printMethod.invoke(printer, "Hello Reflection!");
// 輸出: Printing: Hello Reflection!
}
}
class Printer {
public void print(String msg) {
System.out.println("Printing: " + msg);
}
}
import java.lang.reflect.Field;
public class ReflectionFieldDemo {
public static void main(String[] args) throws Exception {
User user = new User("Alice", 25);
Class> clazz = user.getClass();
// 獲取聲明字段 (包含 private,不含繼承)
Field nameField = clazz.getDeclaredField("name");
Field ageField = clazz.getDeclaredField("age");
// 關鍵: 破壞封裝,允許訪問私有成員
nameField.setAccessible(true);
ageField.setAccessible(true);
// 讀取
System.out.println("原始: " + nameField.get(user) + ", " + ageField.get(user));
// 修改
nameField.set(user, "Bob");
ageField.set(user, 30);
System.out.println("修改後: " + user); // User{name='Bob', age=30}
}
}
class User {
private String name;
private int age;
public User(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { return "User{name='" + name + "', age=" + age + "}"; }
}
import java.lang.reflect.Constructor;
public class ReflectionNewInstanceDemo {
public static void main(String[] args) throws Exception {
Class clazz = User.class;
// 無參構造器 (要求類有 public 無參構造器)
User u1 = clazz.getDeclaredConstructor().newInstance();
// 或 clazz.newInstance() (JDK 9 棄用)
// 帶參構造器
Constructor ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // 若構造器非 public
User u2 = ctor.newInstance("Charlie", 28);
System.out.println(u1); // User{name='null', age=0}
System.out.println(u2); // User{name='Charlie', age=28}
}
}
import java.lang.reflect.Field;
import java.util.Arrays;
public class BeanCopier {
public static void copyProperties(Object source, Object target) throws Exception {
Class> srcClass = source.getClass();
Class> tgtClass = target.getClass();
// 獲取源對象所有聲明字段
Field[] srcFields = srcClass.getDeclaredFields();
// 批量設置可訪問 (性能優於逐個 setAccessible)
java.lang.reflect.AccessibleObject.setAccessible(srcFields, true);
for (Field srcField : srcFields) {
String name = srcField.getName();
try {
// 目標對象查找同名字段
Field tgtField = tgtClass.getDeclaredField(name);
tgtField.setAccessible(true);
// 類型兼容檢查 (簡化版)
if (tgtField.getType().isAssignableFrom(srcField.getType())) {
Object value = srcField.get(source);
tgtField.set(target, value);
}
} catch (NoSuchFieldException ignored) {
// 目標無此字段,跳過
}
}
}
public static void main(String[] args) throws Exception {
User src = new User("Source", 99);
User tgt = new User("Target", 0);
copyProperties(src, tgt);
System.out.println(tgt); // User{name='Source', age=99}
}
}
| 方法 | 返回 | 範圍 | 訪問權限 |
|---|---|---|---|
getMethod(name, params) | Method | public (含繼承) | 僅 public |
getDeclaredMethod(name, params) | Method | 本類聲明 (含私有) | 需 setAccessible(true) |
getFields() | Field[] | public (含繼承) | 僅 public |
getDeclaredFields() | Field[] | 本類聲明 (含私有) | 需 setAccessible(true) |
getConstructors() | Constructor[] | public | 僅 public |
getDeclaredConstructors() | Constructor[] | 本類聲明 (含私有) | 需 setAccessible(true) |
Method/Field/Constructor 對象 (它們是不可變線程安全的)。MethodHandle、JDK 9+ VarHandle 提供更接近直接調用的性能。setAccessible(true) 破壞封裝,可能被安全管理器禁止。opens package to module 導出才能反射私有成員。reflect-config.json,否則運行期報錯。Java 反射是框架級能力,業務代碼慎用。掌握 Class 獲取、Method/Field/Constructor 核心三件套、setAccessible(true) 破壞封裝、泛型擦除下的 Type 體系補完,能讓你讀懂主流框架源碼、編寫通用工具類、實現插件化架構。記住:緩存反射對象、避開熱點路徑、拥抱編譯期代碼生成。