Java反射
一、反射是什么
反射(Reflection) 是Java赋予程序在运行时动态“自省”和“操作”类的能力。打个比方:
常规编码是“编剧”写死剧本(
new User()),而反射是“导演”在开拍现场拿着剧本(Class对象),现场决定用哪个演员、调哪句台词,甚至临时修改台词。
二、反射能干什么
通过 java.lang.Class 和 java.lang.reflect 包下的API,你能做到:
- “窥探”类的全貌 :在运行时获取类的构造器、方法、字段、注解、父类、实现的接口。
- “凭空”创建对象 :调用
Class.forName("xxx").newInstance()或通过构造器动态创建实例(不需要写new关键字)。 - “调用”任意方法 :调用对象上的任何方法,包括私有方法(配合
setAccessible(true)打破封装)。 - “修改”任意属性 :给对象字段赋值,即使是
private static final理论上也能强行改写(虽然极不推荐)。
三、反射有哪些应用场景
1. 主流框架的“灵魂”——IOC(控制反转)和依赖注入
Spring在启动时,会扫描带有 @Component、@Service 的类。
- 通过反射获取类的 无参构造器 ,动态创建Bean实例。
- 扫描类中带有
@Autowired的字段,通过反射调用field.set(bean, dependency)把依赖注入进去,而不用你手动写set方法。
2. JSON/XML 序列化与反序列化(Jackson / Gson / Fastjson)
当你写 gson.fromJson(jsonStr, User.class) 时:
- 底层通过反射获取
User类的所有字段名和类型 (如String name)。 - 遍历JSON键值对,用反射调用
field.set(obj, jsonValue),把数据填进对象的私有字段里。它根本不需要你提供setter方法也能赋值(通过setAccessible(true))。
3. JDBC 与 ORM 框架(MyBatis / Hibernate)
MyBatis 将数据库的 ResultSet(结果集)映射到你的 POJO 时:
- 读取
User类的字段名(如userName)。 - 从
ResultSet中取rs.getObject("user_name")。 - 通过反射
method.invoke(user, value)调用setUserName()方法,将数据逐行注入。这就是为什么你只写个接口和XML,就能把数据库数据变出Java对象。
4. 动态代理(AOP 面向切面编程的核心)
Spring AOP(事务管理、日志监控)底层依赖动态代理。
- 运行时通过反射生成目标类的 代理子类 。
- 当你调用
userService.save()时,代理类用反射拦截方法调用,先开启事务(invoke前织入逻辑),再调用原方法(invoke转发),最后提交事务(invoke后织入逻辑)。
5. 开发工具与调试(IDEA 智能提示、JUnit 单元测试)
- IDEA 能弹出
.后的方法列表,是因为它用反射实时解析当前变量的Class信息。 - JUnit 执行测试时,反射扫描所有带有
@Test注解的方法,并动态调用它们。你的main方法本质上也是JVM用反射加载启动类的。
四、反射具体该怎么使用
// 这个类没有实现任何接口,包含各种访问修饰符
public class Student {
// 私有字段 + 静态字段
private String name;
private int age;
public static String school = "北京大学";
// 无参构造(public)
public Student() {
System.out.println("调用了无参构造");
}
// 有参构造(private)
private Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("调用了私有有参构造:" + name + ", " + age);
}
// 公有方法
public void publicMethod(String msg) {
System.out.println("公有方法执行,消息:" + msg + ",name=" + name);
}
// 私有方法(带返回值)
private String privateMethod(int x) {
System.out.println("私有方法执行,入参:" + x);
return "私有方法返回:" + (x * 10);
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// -------- 1. 获取 Class 对象 ----------
Class<?> clazz = Class.forName("com.hzp.pk4.Student");
System.out.println("类名:" + clazz.getName());
// -------- 2. 造对象(调用构造器) ----------
// 2.1 调用无参构造(最常用)
Student student = (Student) clazz.getDeclaredConstructor().newInstance();
System.out.println("反射创建的对象1:" + student);
// 2.2 调用私有有参构造(模拟框架反序列化时强行创建对象)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class, int.class);
privateConstructor.setAccessible(true); // 暴力破解私有权限!
Student student2 = (Student) privateConstructor.newInstance("张三", 18);
System.out.println("反射创建的对象2:" + student2);
// -------- 3. 调方法 ----------
// 3.1 调用公有方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);
publicMethod.invoke(student, "Hello Reflect"); // 第一个参数是实例,后面是实参
// 3.2 调用私有方法(并且获取返回值)
Method privateMethod = clazz.getDeclaredMethod("privateMethod", int.class);
privateMethod.setAccessible(true); // 必须破解,否则报 IllegalAccessException
String result = (String) privateMethod.invoke(student, 5);
System.out.println("私有方法返回值:" + result);
// -------- 4. 改属性(修改私有字段) ----------
// 4.1 修改私有实例字段 name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(student, "李四"); // 直接把 student 的 name 改为 "李四"
System.out.println("修改后的 student:" + student);
// 4.2 修改私有实例字段 age
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(student, 25); // 用 setInt 避免装箱开销
System.out.println("修改年龄后的 student:" + student);
// 4.3 修改公有静态字段 school(静态字段传 null 作为对象实例)
Field schoolField = clazz.getField("school"); // 公有字段用 getField,不用 getDeclaredField
schoolField.set(null, "清华大学"); // 静态传 null
System.out.println("修改后的学校:" + Student.school);
}
}

浙公网安备 33010602011771号