OpenJDK 与 Oracle JDK 的双亲委派机制完全一致,核心逻辑实现在 ClassLoader.loadClass 方法中,由 Launcher 初始化 AppClassLoader、ExtClassLoader 并串联为 App→Ext→Bootstrap 链,且仅当重写 loadClass 并跳过 super.loadClass 调用时才真正破坏委派。

OpenJDK 的双亲委派机制和 Oracle JDK 完全一致,不是“不同实现”,而是同一套规范在 OpenJDK 源码中的具体落地——关键逻辑就藏在 ClassLoader.loadClass 方法里,且所有标准加载器(AppClassLoader、ExtClassLoader)都由 sun.misc.Launcher 初始化。
loadClass 方法是双亲委派的实际执行者
你写的任何类,只要没被自定义加载器绕过,最终都会走到 ClassLoader.loadClass(String name, boolean resolve)。这个方法的逻辑就是双亲委派的完整脚本:
- 先调用
findLoadedClass(name)检查是否已加载(避免重复) - 若未加载,且当前加载器有父加载器(
parent != null),就递归调用parent.loadClass(name, false) - 若父为
null(即到达ExtClassLoader的父级),则调用findBootstrapClassOrNull(name)—— 这个 native 方法会去rt.jar等启动路径查类,但 Java 层无法直接拿到启动类加载器实例(getClass().getClassLoader()对java.lang.String返回null) - 所有上级都失败后,才调用
findClass(name),而这个方法默认抛ClassNotFoundException,必须由子类(如URLClassLoader)重写才能真正从文件或网络读取字节码
Launcher 是三个标准加载器的“组装车间”
OpenJDK 启动时,sun.misc.Launcher 的静态初始化块会按顺序创建并串联加载器:
-
ExtClassLoader被创建时,其parent字段被设为null,但它在loadClass中遇到null时会 fallback 到findBootstrapClassOrNull -
AppClassLoader创建时,显式把ExtClassLoader实例传给父构造器,形成App → Ext → Bootstrap链 - 你通过
ClassLoader.getSystemClassLoader()拿到的,就是这个AppClassLoader实例;它的getClass().getClassLoader()返回ExtClassLoader,再往上就断了
常见破坏双亲委派的坑:不是“绕过”,而是“没走对路”
很多人以为自定义加载器天然打破双亲委派,其实错在细节:
- 如果你继承
ClassLoader但没重写loadClass,只重写了findClass,那它依然遵守双亲委派——因为父类的loadClass逻辑没动 - 真正打破的写法是:在子类中覆写
loadClass,且**跳过 super.loadClass() 调用**,直接调用findClass或自己解析字节码(比如 Tomcat 的WebAppClassLoader就是这么干的,优先加载WEB-INF/classes) - 另一个隐形坑:
Thread.currentThread().getContextClassLoader()默认是AppClassLoader,但某些框架(如 JNDI、SPI)会用它加载资源——如果线程上下文被意外替换为一个非委派加载器,就会触发类找不到或 LinkageError
最易被忽略的一点:双亲委派只管“加载”,不管“链接”和“初始化”。resolveClass(c) 在 loadClass 末尾才触发,而类的静态块执行、字段赋值等初始化动作,是在首次主动使用该类时才发生——这意味着,即使你成功加载了一个类,它也可能在后续第一次调用时因父加载器里已有同名类而报 LinkageError(比如两个 classloader 都加载了 com.example.Utils,但它们的 toString() 返回类型不兼容)。











