ClassLoader.loadClass() 仅执行类加载与链接,不触发初始化;其通过双亲委派查找类,父加载器失败后由findClass定位字节码并经defineClass注册为Class对象,且Class与加载器绑定不可变。

通过 ClassLoader 获取 Class 对象,本质是触发 JVM 类加载流程中“加载”阶段的主动调用,其底层逻辑围绕双亲委派、字节码定位、内存注册与 Class 对象创建四步展开,不直接触发类初始化。
双亲委派是 loadClass 的默认行为
调用 loader.loadClass("com.example.User") 时,ClassLoader 的 loadClass(String, boolean) 方法会严格按顺序执行:
- 先检查该类是否已被当前加载器或其任意父加载器加载(
findLoadedClass) - 若未加载,且当前加载器有父加载器,则委托父加载器递归调用
loadClass - 直到 Bootstrap ClassLoader(由 C++ 实现,无 Java 父类),它尝试从
$JAVA_HOME/jre/lib加载核心类;若失败,再逐级回退 - 所有父级都未找到时,才由当前加载器执行
findClass(name)
findClass 是自定义加载逻辑的入口
findClass 是一个受保护的模板方法,JDK 默认实现直接抛出异常,必须由子类重写。它的职责是:
- 根据类名构造路径(如将
com.example.User转为/com/example/User.class) - 从指定来源读取字节码:本地文件系统、JAR 包、网络流、数据库 BLOB 等
- 将字节数组传给
defineClass(name, b, off, len),由 JVM 验证格式并生成内部类结构 -
defineClass返回的Class对象会被缓存到当前加载器的classes成员中,确保同一加载器下类的唯一性
ClassLoader 与 Class 对象的绑定关系
每个 Class 对象内部都持有一个对加载它的 ClassLoader 的弱引用(可通过 clazz.getClassLoader() 获取)。这个绑定在类加载完成时即确立,不可更改。关键点包括:
- 同一个类名,被不同
ClassLoader实例加载,会产生两个不相等的Class对象(==和equals均返回false) - 类的静态变量、
方法、注解解析等,都与该ClassLoader的运行时上下文绑定 - 若加载器被回收,且无其他强引用指向其加载的
Class,这些Class可能被卸载(需满足 JVM 卸载条件)
和 forName() 的关键区别
ClassLoader.loadClass() 与 Class.forName(String) 表面相似,但行为差异显著:
-
loadClass()默认不触发类的初始化(即不执行),仅完成加载+链接(验证、准备、解析) -
Class.forName(name)底层调用的是Class.forName(name, true, currentClassLoader),第二个参数true表示强制初始化 - 因此,若只希望获取
Class对象而不运行静态块或赋值语句,应优先使用ClassLoader.loadClass()











