反射:访问字段与方法
通过反射读写字段、调用方法、访问构造方法
反射:访问字段与方法
上章讲了如何获取 Class 对象来探查类信息。这一章讲更强大的——通过反射在运行时动态操作对象:读写字段、调用方法,甚至访问 private 成员。
学完本章你将: 用反射读写字段、调用方法、创建实例。
为什么需要反射操作
正常写代码时你明确知道类名和方法名。但框架不知道——Spring 需要把配置文件里的字段名注入到对象,JUnit 需要找到所有带 @Test 的方法来执行。反射就是"在不知道类名/方法名的情况下,动态找到并调用它们"的能力。
访问字段
java
class Person {
public String name; // public
private int age; // private
}
Person p = new Person();
Class<?> clazz = p.getClass();
// 访问 public 字段
Field nameField = clazz.getField("name"); // 只能拿 public
nameField.set(p, "Alice");
System.out.println(nameField.get(p)); // "Alice"
// 访问 private 字段 —— 需要 setAccessible
Field ageField = clazz.getDeclaredField("age"); // 拿任意字段(包括 private)
ageField.setAccessible(true); // "撬锁"——绕过 private 限制
ageField.set(p, 25);
System.out.println(ageField.get(p)); // 25
⚠️ getField() vs getDeclaredField():
getField只能拿 public 字段(包括继承的),getDeclaredField能拿本类所有字段(public + private + protected)。私有字段需要setAccessible(true)才能读写。
调用方法
同样的模式应用于方法:找到 Method 对象,然后 invoke 调用。
java
class Calculator {
public int add(int a, int b) { return a + b; }
private String secret() { return "密文"; }
}
Calculator calc = new Calculator();
Class<?> clazz = calc.getClass();
// 调用 public 方法 —— 需要方法名 + 参数类型
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result = addMethod.invoke(calc, 3, 5); // 调用 calc.add(3, 5)
System.out.println(result); // 8
// 调用 private 方法
Method secretMethod = clazz.getDeclaredMethod("secret");
secretMethod.setAccessible(true); // 撬锁
System.out.println(secretMethod.invoke(calc)); // "密文"
invoke 的参数: 第一个是目标对象(静态方法传 null),后面是方法参数。返回值是 Object,需要自己强转。
通过反射创建对象
java
// 获取带参构造方法
Constructor<StringBuilder> cons =
StringBuilder.class.getConstructor(String.class);
StringBuilder sb = cons.newInstance("Hello");
System.out.println(sb); // Hello
// 无参构造
Constructor<ArrayList> noArg =
ArrayList.class.getConstructor();
ArrayList<?> list = noArg.newInstance();
💡 反射的性能代价: 反射比直接调用慢一个数量级,因为它绕过了 JIT 编译优化。框架只在启动时用反射(扫描类、注入依赖),运行时走直接调用。自己写代码时优先用直接调用。