JavaSE | 15-反射与动态代理

本文更新于 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

操作目标对应类核心方法
构造方法ConstructornewInstance(参数) :创建对象
成员变量Fieldset(对象, 值) / get(对象)
成员方法Methodinvoke(对象, 参数) :运行方法

反射获取构造方法

方法名作用备注
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...)获取指定的方法用于精确锁定本类某个方法。

总结:反射的三步

  1. 获取 Class 对象Class clazz = Class.forName("全类名");

  2. 获取成员对象

    • 找构造:getDeclaredConstructor()

    • 找属性:getDeclaredField("变量名")

    • 找方法:getDeclaredMethod("方法名", 参数.class)

  3. 暴力拆解(针对 private)

    • 执行 setAccessible(true);
  4. 实际操作

    • 构造 $\rightarrow$ newInstance()

    • 属性 $\rightarrow$ set() / get()

    • 方法 $\rightarrow$ invoke()

动态代理

  1. 无侵入性:不需要修改 BigStar 的任何代码,就能给它增加功能。

  2. JDK 代理限制:只能代理实现了接口的类。如果一个类没接口,需要使用 CGLIB(通过继承实现代理)。

  3. 应用场景

    • 日志记录:每个方法执行前打印参数,执行后打印耗时。

    • 权限校验:执行方法前检查用户是否登录。

    • 事务管理: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("太空步");
    }
}
今日访问 ... 次 | 今日访客 ... 人 | 本页阅读 ...
小站已萌萌哒运行了 0 0 0
已累计耕耘 16 篇博文 · 共 42.69k 个字
总访问量 ...