Java 反射机制详解(二)——Field、Method、Constructor操作
·
上一篇讲清楚了反射的原理和 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)本身不需要。
如果本文对你有帮助,欢迎点赞收藏。有问题欢迎在评论区交流!
更多推荐
所有评论(0)