相信大家都用过 JDK 中的动态代理功能。我们从 class 文件来看看,JDK 所生成的代理类长什么样子。
代码
请将以下代码保存为 MyProxy.java ⬇️
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class MyProxy {
public static void main(String[] args) {
InvocationHandler invocationHandler = (proxy, method, params) -> {
System.out.println("Hello world");
return null;
};
Runnable runnable = (Runnable) Proxy.newProxyInstance(
MyProxy.class.getClassLoader(), new Class<?>[]{Runnable.class}, invocationHandler);
runnable.run();
}
}
编译与运行
用以下命令可以编译 MyProxy.java (我是用的 JDK 版本是 21)
bash
javac -parameters MyProxy.java
编译后会生成 MyProxy.class 文件。执行以下命令可以运行 MyProxy 类中的 main 方法 ⬇️
bash
java MyProxy
运行结果如下 ⬇️
text
Hello world
将代理类保存至 class 文件中
如果想看到 JDK 所生成的代理类的内容,可以执行如下命令
bash
java -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true MyProxy
至于为什么使用这个选项就可以将代理类保存到 class 文件里,可以参考以下代码 ⬇️ (以下截图来自 JDK 25 的源码)

执行该命令后,当前目录下会有新的目录/文件生成。执行 tree . 命令,可以看到如下结果
text
.
├── jdk
│ └── proxy1
│ └── $Proxy0.class
├── MyProxy.class
└── MyProxy.java
3 directories, 3 files
结果分析
通过执行 javap -v -p jdk.proxy1.\$Proxy0 命令可以查看 $Proxy0.class 文件的内容,但是自己去反编译太花时间了,而且很容易出错,我们直接借助 Intellij IDEA 来看 $Proxy0.class 文件的内容。完整的结果如下
java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package jdk.proxy1;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Runnable {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void run() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
ClassLoader var0 = $Proxy0.class.getClassLoader();
try {
m0 = Class.forName("java.lang.Object", false, var0).getMethod("hashCode");
m1 = Class.forName("java.lang.Object", false, var0).getMethod("equals", Class.forName("java.lang.Object", false, var0));
m2 = Class.forName("java.lang.Object", false, var0).getMethod("toString");
m3 = Class.forName("java.lang.Runnable", false, var0).getMethod("run");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}
为了便于理解,我画了张简略的类图 ⬇️

可以这样概括, jdk.proxy1.$Proxy0 中的 m0/m1/m2/m3 字段分别与以下方法对应
hashCode()方法(来自java.lang.Object)equals(Object)方法(来自java.lang.Object)toString()方法(来自java.lang.Object)run()方法(来自java.lang.Runnable)
在 jdk.proxy1.$Proxy0 的 static 语句块里,会为 m0/m1/m2/m3 字段赋值。
其他
画 "jdk.proxy1.$Proxy0 的类图" 所用到的代码
我借助了 PlantUML 的插件来画那张图,用到的代码如下
puml
@startuml
'https://plantuml.com/class-diagram
title <i>jdk.proxy1.$Proxy0</i> 的类图
caption 请注意: 图中只画了本文关心的字段/方法
interface java.io.Serializable
class java.lang.reflect.Proxy
interface java.lang.Runnable
class jdk.proxy1.$Proxy0
java.io.Serializable <|.. java.lang.reflect.Proxy
java.lang.reflect.Proxy <|-- jdk.proxy1.$Proxy0
java.lang.Runnable <|.. jdk.proxy1.$Proxy0
class java.lang.reflect.Proxy {
# InvocationHandler h
- Proxy()
# Proxy(InvocationHandler h)
}
interface java.lang.Runnable {
void run()
}
class jdk.proxy1.$Proxy0 {
- {static} final Method m0
- {static} final Method m1
- {static} final Method m2
- {static} final Method m3
+ $Proxy0(InvocationHandler var1)
+ final int hashCode()
+ final boolean equals(Object var1)
+ final String toString()
+ final void run()
}
note left of jdk.proxy1.$Proxy0::hashCode
<code>
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (...) {
}
</code>
end note
note left of jdk.proxy1.$Proxy0::equals
<code>
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (...) {
}
</code>
end note
note left of jdk.proxy1.$Proxy0::toString
<code>
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (...) {
}
</code>
end note
note left of jdk.proxy1.$Proxy0::run
<code>
public final void run() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (...) {
}
</code>
end note
note bottom of jdk.proxy1.$Proxy0
<i>jdk.proxy1.$Proxy0</i> 类里有如下的 <i>static</i> 语句块
<code>
static {
ClassLoader var0 = $Proxy0.class.getClassLoader();
try {
m0 = Class.forName("java.lang.Object", false, var0).getMethod("hashCode");
m1 = Class.forName("java.lang.Object", false, var0).getMethod("equals", Class.forName("java.lang.Object", false, var0));
m2 = Class.forName("java.lang.Object", false, var0).getMethod("toString");
m3 = Class.forName("java.lang.Runnable", false, var0).getMethod("run");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(((Throwable)var2).getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(((Throwable)var3).getMessage());
}
}
</code>
end note
@enduml