Class
Class 源码分析
代码块收起展开
class 是反射的根:JVM 每加载一个类型(类、接口、数组、基本类型、void),就配一个唯一的 class 对象,作为方法区/元空间里类型元数据在 Java 侧的句柄。
拿字段、拿方法、造实例,一切反射操作都从它出发;「是不是同一个类」的判定标准也是它——同名 + 同一个定义类加载器,才是同一个 class 对象,`==` 直接可比。它不能被 Java 代码 new,全部由 VM 在类加载时创建。
代码块收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.lang.Class
public final class Class<T> implements java.io.Serializable,
GenericDeclaration, // 类自己也能声明类型参数 <T>,所以和 Method/Constructor 同属泛型声明体系
Type,
AnnotatedElement, // getAnnotation 一族的入口
TypeDescriptor.OfField<Class<?>>,
Constable { // Class 可作为常量进常量池(condy/ldc)
// ...
/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader, Class<?> arrayComponentType, char mods, ProtectionDomain pd, boolean isPrim) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
// The following assignments are done directly by the VM without calling this constructor.
classLoader = loader; // 这个构造器一次都不会真的被调用:VM 创建 Class 时绕过它直接填字段,它只是防止编译器生成默认构造器
componentType = arrayComponentType;
modifiers = mods;
protectionDomain = pd;
primitive = isPrim;
}
// ...
// Initialized in JVM not by private constructor
// This field is filtered from reflection access, i.e. getDeclaredField
// will throw NoSuchFieldException
private final ClassLoader classLoader; // 定义类加载器(见 [ClassLoader](/articles/sourcecode/java/jvm/classloader));被反射过滤,getDeclaredField("classLoader") 会 NoSuchFieldException
// ...
private final transient char modifiers; // Set by the VM // 修饰符位掩码,16 位够用所以存 char
private final transient boolean primitive; // Set by the VM if the Class is a primitive type.
// ...
private transient final Class<?> componentType; // 数组类型才非空,"是不是数组"就看它
// ...
@IntrinsicCandidate
public native boolean isInstance(Object obj); // instanceof 的动态版:类型运行期才确定时用它。JIT intrinsic,热路径会内联成和 instanceof 一样的机器码
@IntrinsicCandidate
public native boolean isAssignableFrom(Class<?> cls); // 判两个 Class 的父子关系;isInstance 判对象和 Class 的关系,方向别记反:A.isAssignableFrom(B) = B 能赋给 A
public boolean isInterface() {
return Modifier.isInterface(modifiers); // 老 JDK 这三个判断都是 native;现在 VM 提前把元数据灌进 final 字段,纯 Java 读取,省掉 JNI 往返且可被 JIT 常量折叠
}
public boolean isArray() {
return componentType != null;
}
public boolean isPrimitive() {
return primitive;
}
// ...
@SuppressWarnings("unchecked")
@IntrinsicCandidate
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj; // 擦除后这个强转本身什么都不查,安全性全靠上一行的 isInstance;null 直接放行,与语言层强转语义一致
}
}拿 Class 对象的三条路殊途同归,拿到的是同一个对象:Xxx.class 编译期写死、obj.getClass() 运行期从对象头找、Class.forName(name) 按字符串现场加载。
前两种不触发类初始化,第三种默认触发——JDBC 老写法 Class.forName("com.mysql.jdbc.Driver") 就是故意借初始化跑 Driver 里的静态注册块。
代码块收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.lang.Class
@CallerSensitive // 返回结果取决于"谁在调我"(用调用者的类加载器),这种方法不能被随便包一层转发,否则 caller 就变了
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass(); // VM 直接读栈帧拿调用者的 Class
return forName(className, caller);
}
// Caller-sensitive adapter method for reflective invocation
@CallerSensitiveAdapter
private static Class<?> forName(String className, Class<?> caller)
throws ClassNotFoundException {
ClassLoader loader = (caller == null) ? ClassLoader.getSystemClassLoader()
: ClassLoader.getClassLoader(caller); // caller==null 只在 JNI 线程直接调进来时发生,栈上没有 Java 帧,兜底用系统加载器
return forName0(className, true, loader, caller); // initialize=true:加载+链接+初始化一条龙
}
// ...
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
throws ClassNotFoundException
{
return forName0(name, initialize, loader, null); // 三参版可以只加载不初始化:框架扫描类信息时传 false,避免扫一遍就把所有 static 块跑了
}
/** Called after security check for system loader access checks have been made. */
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException; // 真正干活的在 VM:查系统字典,没有就委托 loader 加载
// ...
@CallerSensitive
@Deprecated(since="9") // 废弃不是因为慢,是异常语义脏:构造器抛的受检异常会被它原样扔出来,绕过编译期检查
public T newInstance()
throws InstantiationException, IllegalAccessException
{
// Constructor lookup
Constructor<T> tmpConstructor = cachedConstructor;
if (tmpConstructor == null) {
// ...
try {
Class<?>[] empty = {};
final Constructor<T> c = getReflectionFactory().copyConstructor(
getConstructor0(empty, Member.DECLARED));
// Disable accessibility checks on the constructor
// access check is done with the true caller
c.setAccessible(true);
cachedConstructor = tmpConstructor = c; // 无参构造器缓存在 Class 对象上,重复调用不再查找
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
try {
Class<?> caller = Reflection.getCallerClass();
return getReflectionFactory().newInstance(tmpConstructor, null, caller);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException()); // 用 Unsafe 把受检异常伪装成非受检抛出——这一行就是它被废弃的原因;替代写法 getDeclaredConstructor().newInstance() 会老实包成 InvocationTargetException
// Not reached
return null;
}
}反射成员查找是三层结构:public API 只负责返回副本,private 层管缓存和继承合并,native 层真正进 VM 掏元数据。
代码块收起展开
// 基于本地 JDK 源码 (D:/1ForCode/JAVA_Source, java.base, JDK 25), java.lang.Class
public Method[] getMethods() {
return copyMethods(privateGetPublicMethods()); // 本类+父类+接口的所有 public 方法
}
// ...
public Method[] getDeclaredMethods() {
return copyMethods(privateGetDeclaredMethods(false)); // 只有本类声明的,含 private,不含继承。记忆:带 Declared = 本类声明含私有不含继承
}
// ...
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException {
Objects.requireNonNull(name);
Method method = getMethod0(name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
return getReflectionFactory().copyMethod(method); // 对外永远给副本,root Method 绝不外泄
}
// ...
// Reflection data caches various derived names and reflective members. Cached
// values may be invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// ...
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount; // 版本戳:类被热替换后靠它识别缓存过期
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
private transient volatile SoftReference<ReflectionData<T>> reflectionData; // 软引用:内存紧张时整套反射缓存可被 GC 回收,反正能从 VM 重建
// Incremented by the VM on each call to JVM TI RedefineClasses()
// that redefines this class or a superclass.
private transient volatile int classRedefinedCount;
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) { // 命中还要对版本号:热替换过就当没缓存,防止拿到旧类的方法列表
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount); // 内部 CAS 安装新缓存,竞争失败就用赢家的,全程无锁
}
// ...
// Returns an array of "root" methods. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
Method[] res;
ReflectionData<T> rd = reflectionData();
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly)); // 真正进 VM 掏元数据的一次 native 调用;filter 把 classLoader 这类敏感成员藏掉
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
return res;
}
// ...
private Method[] privateGetPublicMethods() {
Method[] res;
ReflectionData<T> rd = reflectionData();
res = rd.publicMethods;
if (res != null) return res;
// No cached value available; compute value recursively.
// Start by fetching public declared methods...
PublicMethods pms = new PublicMethods();
for (Method m : privateGetDeclaredMethods(/* publicOnly */ true)) {
pms.merge(m); // merge 按签名去重并保留更具体的:子类覆写压过父类,类方法压过接口默认方法
}
// ...then recur over superclass methods...
Class<?> sc = getSuperclass();
if (sc != null) {
for (Method m : sc.privateGetPublicMethods()) {
pms.merge(m);
}
}
// ...and finally over direct superinterfaces.
for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {
for (Method m : intf.privateGetPublicMethods()) {
// static interface methods are not inherited
if (!Modifier.isStatic(m.getModifiers())) { // 接口 static 方法不参与继承,语言规则在反射里的镜像
pms.merge(m);
}
}
}
res = pms.toArray();
rd.publicMethods = res; // 合并结果也缓存,getMethods 的递归代价只付一次
return res;
}
// ...
private static Method[] copyMethods(Method[] arg) {
Method[] out = new Method[arg.length];
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyMethod(arg[i]); // 每次调用都整批复制:你对返回值 setAccessible(true) 只污染自己那份
}
return out;
}
private native Field[] getDeclaredFields0(boolean publicOnly);
private native Method[] getDeclaredMethods0(boolean publicOnly); // 元数据真身在 VM 的 InstanceKlass 里,Java 侧所有数组都只是缓存+包装
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);原理串讲
走一遍最经典的链路:Class.forName("com.mysql.cj.jdbc.Driver")。
入口方法标了 @CallerSensitive,第一步 Reflection.getCallerClass() 让 VM 直接读栈帧,拿到调用方的 Class,再从它身上取出定义类加载器传给 forName0。
为什么非要用调用者的加载器?因为类名字符串本身不唯一确定一个类——同一个名字在不同加载器下可以是完全不同的两个类(Tomcat 里每个 webapp 一个加载器就是靠这个隔离的)。
如果 forName 固定用系统加载器,webapp 自带的 jar 里的类它根本看不见。
所以”谁调的就用谁的加载器”是唯一符合直觉的语义,代价是这个方法变成 caller 敏感方法,不能被中间层随便代理转发。
forName0 是 native,进 VM 查系统字典,没加载过就走加载+链接,initialize=true 再把类初始化做掉——Driver 的 static 块在这一步执行,向 DriverManager 注册自己。
这就是 JDBC 4.0 之前那行仪式性代码的全部意义。
第二条链路:clazz.getMethod("connect", String.class, Properties.class)。
调用顺序是 getMethod → getMethod0 → getMethodsRecursive(本类 declared 没有就递归父类和接口,选最具体的)→ privateGetDeclaredMethods → reflectionData()。
缓存层的设计值得停一下:reflectionData 是 SoftReference<ReflectionData>,且 ReflectionData 里记了创建时的 classRedefinedCount。
为什么用软引用而不是直接挂强引用?因为反射元数据是纯派生数据——丢了随时可以再调一次 getDeclaredMethods0 从 VM 重建,但一个大型应用几万个类的 Method/Field 数组很占内存。
软引用让这块缓存在内存紧张时可以被 GC 整体回收,拿内存换一次重建。为什么还要版本号?因为 Instrumentation/JVM TI 的 RedefineClasses 可以在运行期改类(热部署、arthas redefine),VM 每重定义一次就把 classRedefinedCount 加一,缓存命中时对不上号就视为过期,用一个 int 比较解决了缓存失效通知问题。
缓存未命中时,getDeclaredMethods0 这个 native 调用进 VM 从 InstanceKlass 掏出方法表,包装成 Method 数组存进缓存。
注意存的是 root 对象,而 getMethod/getMethods 返回前一定过一道 copyMethod。
为什么每次都复制?因为 Method 上有可变状态——accessible 标志。root 是全局共享的,如果直接返回,任何人 setAccessible(true) 一下,所有拿到同一个 Method 的代码都会跟着跳过访问检查,等于全局后门。
复制之后每个调用方改的是自己的副本;而副本和 root 共享底层的 MethodAccessor(真正执行 invoke 的东西),所以复制不损失调用性能。
这也解释了为什么高频反射调用要自己把 Method 缓存成字段:每次 getMethod 都有一次数组复制和查找,省不掉。
最后是造实例。newInstance() 从 JDK 9 起废弃,源码里能看到原因:它查到无参构造器后先 setAccessible(true) 缓存起来,调用时若构造器抛异常,catch 块里用 Unsafe.throwException 把受检异常原样往外扔——你的代码没法 catch 一个编译器不知道会抛的受检异常。
替代写法 getDeclaredConstructor().newInstance() 会把构造器异常包进 InvocationTargetException,异常类型信息不丢。
设计取舍
forName默认初始化,ClassLoader.loadClass只加载不链接不初始化;框架扫类用后者或三参forName(name, false, loader),避免扫描阶段触发一堆 static 块。- 类型判断三剑客方向别混:
obj instanceof A编译期已知类型;A.isInstance(obj)运行期动态版;A.isAssignableFrom(B)判两个 Class 的关系,B 的实例能赋给 A。 getXxx与getDeclaredXxx:带 Declared 的是”本类声明、含私有、不含继承”,不带的是”public、含继承”。要拿父类私有字段只能沿getSuperclass()手动向上爬。- 反射结果都是副本:
setAccessible只影响自己那份,这是安全设计不是 bug;同时意味着getMethod不是免费的,热路径应缓存 Method 对象而不是缓存 Class。 - JDK 24/25 把
isInterface/isArray/isPrimitive从 native 改成读 VM 预填的 final 字段:JNI 往返在这种一行判断上比逻辑本身贵几个数量级,元数据前置到 Java 堆是纯赚的优化。