**Java 双亲委派机制(Parent Delegation Model)**一句话:
类加载时,优先让父类加载器去加载,只有父加载器找不到,子加载器才自己尝试。
这是 JVM 类加载体系的安全与一致性基础。
一、类加载器层级结构
从上到下:
-
Bootstrap ClassLoader
- C++实现
- 加载:
rt.jar/java.lang.*等核心类
-
Extension ClassLoader(JDK9+ 为 PlatformClassLoader)
- 加载:
$JAVA_HOME/lib/ext(或模块平台类)
- 加载:
-
Application ClassLoader
- 加载:classpath 下的类(你写的业务代码)
-
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)会通过线程上下文类加载器或自定义加载器打破双亲委派。