Z
ZHANK
编程在线教程Java 教程反射:访问字段与方法
反射与注解

反射:访问字段与方法

通过反射读写字段、调用方法、访问构造方法

反射:访问字段与方法

上章讲了如何获取 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 编译优化。框架只在启动时用反射(扫描类、注入依赖),运行时走直接调用。自己写代码时优先用直接调用。