上一篇讲清楚了反射的原理和 Class 对象的获取,这一篇总结:拿到 Class 对象之后,怎么获取字段、调用方法、创建对象


一、快速回顾:

上一篇说过,反射的一切操作都从 Class 对象开始:

Class<?> clazz = Class.forName("com.example.UserService");

接下来,我们用这个 clazz 对象来做三件事:获取字段、获取方法、获取构造器


二、Declared 与 Non-Declared

在反射 API 里,几乎所有获取类信息的方法都有两个版本,理解这个区别是用好反射的前提。

2.1 成员变量(Field)

方法 能获取的范围
getFields() 仅能获取本类及父类的 public 字段
getDeclaredFields() 获取本类的所有字段(包括 private、protected、默认)
getField("name") 获取指定名称的 public 字段
getDeclaredField("name") 获取指定名称的字段,无视修饰符
Class<?> clazz = Class.forName("com.example.User");

// 获取所有字段(含 private)
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName() + " : " + field.getType());
}

// 获取指定字段
Field nameField = clazz.getDeclaredField("username");

2.2 方法(Method)

方法 能获取的范围
getMethods() 获取本类及父类的所有 public 方法(含继承自 Object 的方法)
getDeclaredMethods() 获取本类声明的所有方法(含 private,但不含父类方法)
getMethod("name", 参数类型...) 获取指定的 public 方法
getDeclaredMethod("name", 参数类型...) 获取指定方法,无视修饰符

注意:获取带参数的方法时,必须传入参数类型的 Class 对象,否则无法唯一确定方法(方法重载)。

// 获取无参方法
Method method1 = clazz.getDeclaredMethod("show");

// 获取带参方法:必须传入参数类型
Method method2 = clazz.getDeclaredMethod("setUsername", String.class);

2.3 构造器(Constructor)

方法 能获取的范围
getConstructors() 获取所有 public 构造器
getDeclaredConstructors() 获取所有构造器,含 private
getConstructor(参数类型...) 获取指定的 public 构造器
getDeclaredConstructor(参数类型...) 获取指定构造器,无视修饰符
// 获取无参构造器
Constructor<?> constructor = clazz.getDeclaredConstructor();

// 获取有参构造器
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class, Integer.class);

2.4 小结

Declared = 本类所有(含 private);不带 Declared = 仅 public(含父类继承)。


三、暴力反射:setAccessible(true)

3.1 什么情况下需要?

当你通过反射操作 private 修饰的成员(字段、方法、构造器)时,直接访问会抛出:

java.lang.IllegalAccessException: ... is not accessible

这时候需要先调用 setAccessible(true) 解除访问限制,这就是暴力反射

3.2 使用规则

Field field = clazz.getDeclaredField("username"); // 获取 private 字段
field.setAccessible(true); // 暴力反射,解除访问限制
// 之后才能正常 get/set

只要是 private 成员,操作前必须先 setAccessible(true),缺一不可。public 成员则无需此步骤。


四、通过反射创建对象

有两种方式:

方式一:通过 Class 直接创建(仅适用于无参构造)

Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();

方式二:通过 Constructor 创建(有参/private 构造器)

// 有参构造
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
Object obj = constructor.newInstance("eric", 18);

// private 构造器(单例模式中常见)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor();
privateConstructor.setAccessible(true); // 暴力反射
Object obj2 = privateConstructor.newInstance();

五、操作成员变量(Field)

5.1 完整流程

// 假设 User 类有 private String username 字段
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.getDeclaredConstructor().newInstance();

// 第一步:获取字段
Field field = clazz.getDeclaredField("username");

// 第二步:如果是 private,暴力反射
field.setAccessible(true);

// 第三步:赋值
field.set(user, "eric");

// 第四步:取值
String value = (String) field.get(user);
System.out.println(value); // eric

5.2 public 字段的操作

public 字段无需暴力反射,直接 get/set:

Field publicField = clazz.getField("publicName"); // 注意:不带 Declared
publicField.set(user, "james");
Object val = publicField.get(user);

5.3 field.get() 和 field.set() 的参数说明

方法 参数 说明
field.get(obj) obj:字段所属的对象实例 获取该对象的这个字段的值
field.set(obj, value) obj:对象实例;value:要设置的值 给该对象的这个字段赋值

六、调用方法(Method)

6.1 完整流程

Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.getDeclaredConstructor().newInstance();

// 获取方法(带参方法必须传参数类型)
Method method = clazz.getDeclaredMethod("setUsername", String.class);

// private 方法需要暴力反射
method.setAccessible(true);

// 执行方法:invoke(对象实例, 参数...)
method.invoke(user, "eric");

6.2 有返回值的方法

invoke 的返回值就是方法执行的结果,强转成对应类型即可:

Method getMethod = clazz.getDeclaredMethod("getUsername");
getMethod.setAccessible(true);
String result = (String) getMethod.invoke(user);
System.out.println(result); // eric

6.3 method.invoke() 参数说明

参数位置 含义
第一个参数 方法所属的对象实例(调用者)
第二个参数起 方法的实参,按顺序传入

七、完整综合示例

把上面所有操作串在一起,模拟 Spring 工厂通过反射创建并操作对象的完整过程:

public class ReflectDemo {
    public static void main(String[] args) throws Exception {

        // 1. 通过字符串类名获取 Class 对象(模拟 Spring 读取 XML 配置)
        Class<?> clazz = Class.forName("com.example.User");

        // 2. 通过反射创建对象(模拟 Spring 创建 Bean)
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object user = constructor.newInstance();

        // 3. 通过反射给 private 字段赋值
        Field usernameField = clazz.getDeclaredField("username");
        usernameField.setAccessible(true);
        usernameField.set(user, "eric");

        Field ageField = clazz.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(user, 18);

        // 4. 通过反射调用方法
        Method showMethod = clazz.getDeclaredMethod("show");
        showMethod.setAccessible(true);
        showMethod.invoke(user);

        // 5. 取值验证
        System.out.println(usernameField.get(user)); // eric
    }
}

八、小结

操作 关键方法 private 成员需要?
获取所有字段 getDeclaredFields() 获取不需要,操作需要 setAccessible(true)
获取指定字段 getDeclaredField("name") 同上
字段赋值 field.set(obj, value) 需要
字段取值 field.get(obj) 需要
获取方法 getDeclaredMethod("name", 参数类型...) 获取不需要,调用需要
调用方法 method.invoke(obj, 参数...) 需要
获取构造器 getDeclaredConstructor(参数类型...) 获取不需要,使用需要
创建对象 constructor.newInstance(参数...) 需要

只要操作(赋值、调用、实例化)private 成员,操作前必须先 setAccessible(true),获取(getDeclaredXxx)本身不需要。


如果本文对你有帮助,欢迎点赞收藏。有问题欢迎在评论区交流!

更多推荐