本文更新于 2026-03-26
反射
原定义:允许对封装类的字段,方法,和构造函数的信息进行编程式访问
反射 允许程序在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性。
正常方式:引入包 $\rightarrow$
new对象 $\rightarrow$ 调用方法。反射方式:获取
Class对象 $\rightarrow$ 解剖类内容 $\rightarrow$ 操作成员。
获取 Class 对象的三种方式
类名.class(最安全,性能最高):
Class clazz = Student.class;- 一般当作参数进行传递
对象.getClass()(已有对象时使用):
Student s = new Student(); Class clazz = s.getClass();Class.forName(“全类名”)(最常用,配置文件加载):
Class clazz = Class.forName("com.mysql.cj.jdbc.Driver");
核心API
| 操作目标 | 对应类 | 核心方法 |
|---|---|---|
| 构造方法 | Constructor | newInstance(参数) :创建对象 |
| 成员变量 | Field | set(对象, 值) / get(对象) |
| 成员方法 | Method | invoke(对象, 参数) :运行方法 |
反射获取构造方法
| 方法名 | 作用 | 备注 |
|---|---|---|
getConstructors() | 获取所有 public 构造 | 只能拿到公有的。 |
getDeclaredConstructors() | 获取 所有 构造 | 包含私有、受保护的。 |
getConstructor(Class...) | 获取指定的 public 构造 | 需传入参数类型的字节码。 |
getDeclaredConstructor(Class...) | 获取指定的构造 | 最常用,可以获取私有构造。 |
反射获取成员变量
| 方法名 | 作用 | 备注 |
|---|---|---|
getFields() | 获取所有 public 变量 | 包含从父类继承的公有变量。 |
getDeclaredFields() | 获取 所有 变量 | 仅限本类定义的(含私有)。 |
getField(String name) | 获取指定的 public 变量 | 根据变量名获取。 |
getDeclaredField(String name) | 获取指定的变量 | 可以获取私有变量。 |
当变量为private时,可以通过
setAccessible(true)设置为可访问
Class clazz = Class.forName("asia.creat.demo2.Student");
Field name = clazz.getDeclaredField("name");
Student s = new Student("张三",23,"男");
name.setAccessible(true);
Object value = name.get(s);
System.out.println(value);
反射获取成员方法
| 方法名 | 作用 | 备注 |
|---|---|---|
getMethods() | 获取所有 public 方法 | 包含本类及父类继承的所有公有方法。 |
getDeclaredMethods() | 获取 所有 方法 | 仅限本类定义的(含私有)。 |
getMethod(String, Class...) | 获取指定的 public 方法 | 需指定方法名和参数类型。 |
getDeclaredMethod(String, Class...) | 获取指定的方法 | 用于精确锁定本类某个方法。 |
总结:反射的三步
获取 Class 对象:
Class clazz = Class.forName("全类名");获取成员对象:
找构造:
getDeclaredConstructor()找属性:
getDeclaredField("变量名")找方法:
getDeclaredMethod("方法名", 参数.class)
暴力拆解(针对 private):
- 执行
setAccessible(true);
- 执行
实际操作:
构造 $\rightarrow$
newInstance()属性 $\rightarrow$
set() / get()方法 $\rightarrow$
invoke()
动态代理
无侵入性:不需要修改
BigStar的任何代码,就能给它增加功能。JDK 代理限制:只能代理实现了接口的类。如果一个类没接口,需要使用 CGLIB(通过继承实现代理)。
应用场景:
日志记录:每个方法执行前打印参数,执行后打印耗时。
权限校验:执行方法前检查用户是否登录。
事务管理:Spring 在方法开始前开启事务,结束后提交或回滚。
// 1. 定义接口(明星的功能清单)
interface Star {
String sing(String name);
void dance(String name);
}
// 2. 定义实现类(真正的明星:被代理对象)
class BigStar implements Star {
private String name;
public BigStar(String name) {
this.name = name;
}
@Override
public String sing(String name) {
System.out.println(this.name + "正在唱歌: " + name);
return "谢谢";
}
@Override
public void dance(String name) {
System.out.println(this.name + "正在跳舞: " + name);
}
// Getter & Setter ...
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
// 3. 创建代理工具类(经纪人生成器)
import java.lang.reflect.Proxy;
class ProxyUtil {
public static Star creatProxy(BigStar bs) {
/*
* Proxy.newProxyInstance 参数说明:
* 1. 类加载器:用于加载生成的代理类
* 2. 接口数组:告诉代理类要实现哪些方法
* 3. 处理器(InvocationHandler):定义代理对象要额外做的“杂事”
*/
Star s = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
(proxy, method, args) -> {
// 这里就是“增强”逻辑(插队干活)
if ("sing".equals(method.getName())) {
System.out.println("【准备】准备话筒,收钱");
} else if ("dance".equals(method.getName())) {
System.out.println("【准备】准备场地,收钱");
}
// 核心步骤:通过反射调用被代理对象(明星)的真实方法
return method.invoke(bs, args);
}
);
return s;
}
}
// 4. 测试类(外部调用)
public class ReflectDemo {
public static void main(String[] args) {
// 1. 创建真实的对象
BigStar bs = new BigStar("张三");
// 2. 获取代理对象(请经纪人)
Star proxy = ProxyUtil.creatProxy(bs);
// 3. 通过代理对象调用方法
String result = proxy.sing("《千里之外》");
System.out.println("返回值: " + result);
System.out.println("--------------------");
proxy.dance("太空步");
}
}