Java双亲委派机制

20 阅读2分钟

**Java 双亲委派机制(Parent Delegation Model)**一句话:

类加载时,优先让父类加载器去加载,只有父加载器找不到,子加载器才自己尝试。

这是 JVM 类加载体系的安全与一致性基础。


一、类加载器层级结构

从上到下:

  1. Bootstrap ClassLoader

    • C++实现
    • 加载:rt.jar / java.lang.* 等核心类
  2. Extension ClassLoader(JDK9+ 为 PlatformClassLoader)

    • 加载:$JAVA_HOME/lib/ext(或模块平台类)
  3. Application ClassLoader

    • 加载:classpath 下的类(你写的业务代码)
  4. Custom ClassLoader

    • 用户自定义(插件、热部署、隔离)

层级是树状(逻辑上),但查找是自上而下。


二、加载流程(双亲委派算法)

当一个类要加载时:

1. 检查缓存(是否已加载)
2. 交给父加载器 loadClass()
3. 父加载器再向上委派...
4. 直到 Bootstrap
5. 若父加载器都找不到
6. 才由当前加载器自己 findClass()

伪代码(ClassLoader.loadClass):

protected Class<?> loadClass(String name, boolean resolve) {
    if (findLoadedClass(name) == null) {
        try {
            if (parent != null)
                c = parent.loadClass(name, false);
            else
                c = findBootstrapClassOrNull(name);
        } catch (ClassNotFoundException e) {}
        if (c == null)
            c = findClass(name);
    }
    return c;
}

三、为什么要双亲委派?

1)安全性:防止核心类被篡改

如果没有双亲委派,你可以自定义一个:

package java.lang;
public class String {}

那 JVM 就可能用你伪造的 String。

双亲委派保证:

所有 java.* 必须由 Bootstrap 加载,业务代码永远覆盖不了。


2)唯一性:核心类全局唯一

保证:

  • java.lang.Object 在 JVM 里只有一份 Class 对象
  • 避免多个加载器各加载一份造成类型不相等

3)稳定性:基础类优先可用

底层依赖(Object、Class、Exception)必须最先被加载。


四、什么时候“打破”双亲委派?

典型场景:

1) Tomcat / 应用服务器(JDBC / SPI)

  • WebAppClassLoader 需要加载自己的 javax.sql.Driver
  • 但接口在父加载器,实现在子加载器
  • 采用 线程上下文类加载器(TCCL)反向委派

2) OSGi / 插件化 / 热部署

  • 每个插件独立 ClassLoader
  • 不完全遵循父优先,而是“按需隔离”

3) 自定义 ClassLoader 重写 loadClass()

例如:

@Override
public Class<?> loadClass(String name) {
    // 先自己找,再给父加载器
}

五、面试标准回答模板

Java 类加载采用双亲委派模型,即类加载请求会先交给父类加载器处理,只有当父加载器无法完成加载时,子加载器才会尝试自己加载。该机制可以防止核心类被篡改,保证基础类的唯一性和安全性。其实现体现在 ClassLoader 的 loadClass 方法中,通过递归委派到 Bootstrap、Platform、Application 等加载器。部分场景(如 Tomcat、SPI)会通过线程上下文类加载器或自定义加载器打破双亲委派。