根本原因在于JDK动态代理基于接口契约而非继承机制,代理类是目标所实现接口的全新实现类,必须通过接口明确方法签名与行为边界,且底层强制依赖Proxy.newProxyInstance传入的interfaces参数生成字节码。

JDK 动态代理必须要求目标类实现接口,根本原因在于它的设计机制——它不代理“类”,而是代理“接口”。
代理对象的本质是接口的实现者
Java 的 java.lang.reflect.Proxy 类在运行时生成的代理类,不是目标类的子类,而是目标类所实现接口的全新实现类。这个代理类与目标类之间没有继承关系,只有“共同实现同一组接口”的契约关系。
因此,Proxy 只需要知道有哪些接口方法要被拦截,它会为这些接口中的每个方法生成对应的代理逻辑(通过 InvocationHandler.invoke() 调度)。如果目标类没实现任何接口,Proxy 就找不到要实现哪些方法,也就无从生成代理类。
没有接口就无法定义代理的行为边界
接口是一组公开、稳定、可契约化的方法声明。JDK 动态代理依赖接口来明确:
立即学习“Java免费学习笔记(深入)”;
- 哪些方法允许被代理(仅限接口中声明的方法);
- 代理对象对外暴露什么能力(即能被转型成什么类型);
- 调用时如何通过反射匹配到具体 method 对象(接口方法签名清晰,无重载歧义)。
若直接代理一个普通类(比如只有 public 方法但没接口),JDK 无法确定哪些方法属于“公开契约”,哪些是内部辅助方法;也无法安全地生成一个能被合法转型、语义一致的代理实例。
技术实现上不支持类级别的字节码生成
JDK 动态代理完全基于 Java 原生反射 API,不操作字节码。它不能像 CGLIB 那样用 ASM 修改或生成子类——Proxy.newProxyInstance() 的参数列表强制要求传入 Class>[] interfaces,底层会校验并仅基于这些接口生成字节码。
即使你提供了一个没有实现类的空接口,JDK 依然能生成代理(如示例中仅声明 Subject 接口就成功创建了 $Proxy0)。这反向印证:它只认接口定义,根本不关心背后有没有真实实现类。
final 类不影响 JDK 代理,进一步说明它不依赖继承
一个 final class UserServiceImpl implements UserService 完全可以被 JDK 代理,因为代理类和它平级,都实现 UserService。这和 CGLIB 必须继承目标类、因而拒绝 final 类形成鲜明对比——差异根源正在于:JDK 代理绕开了继承,只靠接口契约协作。


















