Java 中的类加载器的双亲委派模型:原理、层级与实现
类加载器的双亲委派模型(Parents Delegation Model)是Java 虚拟机(JVM)中类加载器协同工作的核心设计原则 ,它定义了类加载器之间的层级关系 和类加载请求的传递规则,是 JVM 保证类加载安全性、避免类重复加载的关键机制。
一、核心定义
一个类加载器在接收到类加载请求 时,不会立即自己尝试加载该类 ,而是先将加载请求向上委派给它的父类加载器 处理;父类加载器会重复这一委派行为,直到请求传递到顶层的启动类加载器 ;只有当父类加载器在自己的类搜索范围内无法找到该类(加载失败) 时,子加载器才会尝试自己去加载这个类。
简单来说:先父后子,父能则父加载,父不能则子加载。
二、Java 默认的类加载器层级(自上而下)
JVM 提供了 3 个核心的内置类加载器,严格遵循双亲委派的层级关系,下层加载器均以上层加载器为 "父加载器"(注:此处的父子并非继承关系,而是组合关系,子加载器持有父加载器的引用):
1.启动类加载器(Bootstrap ClassLoader)
顶层加载器,由C/C++ 实现(并非 Java 类),属于 JVM 内核的一部分;
负责加载 Java 核心类库,对应路径为JAVA_HOME/jre/lib(如rt.jar、core.jar,包含java.lang、java.util等核心包);
该加载器没有父加载器,是委派链的终点。
2.扩展类加载器(Extension ClassLoader)
由 Java 实现(sun.misc.Launcher$ExtClassLoader),父加载器为启动类加载器;
负责加载 Java 扩展类库,对应路径为JAVA_HOME/jre/lib/ext,或系统属性java.ext.dirs指定的目录;
加载启动类加载器未覆盖的扩展功能类。
3.应用程序类加载器(Application ClassLoader)
由 Java 实现(sun.misc.Launcher$AppClassLoader),父加载器为扩展类加载器;
也叫系统类加载器 ,是程序运行时默认的类加载器;
负责加载用户自定义的类 和项目依赖的第三方类库,对应路径为系统属性java.class.path(即我们常说的classpath)。
4.简单图解:

三、双亲委派模型的核心作用(设计意义)
该模型的设计完全围绕JVM 的类加载安全 和类的唯一性,核心作用有两点:
-
保证 Java 核心类的安全性,防止核心 API 被篡改
核心类(如
java.lang.String、java.lang.Object)只能由启动类加载器 加载,若用户自定义一个同名的java.lang.String类,由于双亲委派规则,加载请求会先委派到启动类加载器,而启动类加载器已加载过核心的String类,用户自定义的同名类不会被加载,避免了恶意代码篡改核心 API 的风险。 -
保证类的唯一性,避免类重复加载
同一个类(全限定类名,如
com.example.Demo)只会被委派链中第一个能加载它的类加载器 加载,后续所有子加载器都会直接使用父加载器加载的类实例,避免了同一个类在 JVM 中被多次加载形成不同的类对象(JVM 中,类的唯一性由 "类加载器 + 全限定类名" 共同决定)。
四、双亲委派模型的通用标准工作流程
无论接收加载请求的是应用程序类加载器(默认)还是自定义类加载器,均遵循以下 6 步核心流程(自定义类加载器的父加载器为应用程序类加载器,流程仅多一层委派,核心逻辑一致):
-
接收请求:某类加载器(如应用程序类加载器)接收到加载指定类(全限定名)的请求;
-
向上委派 :该加载器不自行加载,立即将加载请求委派给自身的父类加载器;
-
逐层传递 :父加载器重复「不自行加载,向上委派」的行为,直到请求传递到顶层的启动类加载器(委派链的终点,无父加载器);
-
顶层尝试加载:启动类加载器在自身专属加载路径( JAVA_HOME/jre/lib ,核心类库目录)中查找该类,尝试完成加载;
- 若找到,直接加载该类,整个流程结束,后续无回退步骤;
- 若未找到,加载失败,请求开始向下回退;
-
中层回退尝试:加载请求回退到启动类加载器的子加载器(扩展类加载器),扩展类加载器在自身加载路径( JAVA_HOME/jre/lib/ext 或 java.ext.dirs 指定目录)中查找该类,尝试完成加载;
- 若找到,直接加载该类,流程结束;
- 若未找到,加载失败,请求继续向下回退;
-
最终子加载器加载 :加载请求回退到最初接收请求的类加载器 ,该加载器在自身专属加载路径中查找并完成加载(若仍未找到,抛出
ClassNotFoundException异常)。
核心规则提炼 :父加载器拥有绝对的加载优先权,只有当所有上层父加载器均无法加载时,子加载器才会执行「自行加载」的逻辑,全程无例外。
双亲委派模型的工作流程图:
找到,加载成功
未找到,加载失败
找到,加载成功
未找到,加载失败
找到,加载成功
未找到,加载失败
- 接收请求
类加载器(应用/自定义)接收到类加载请求 2. 向上委派
不自行加载,委派给自身父加载器 3. 逐层传递
父加载器重复委派,直到顶层启动类加载器 4. 顶层尝试加载
启动类加载器在JAVA_HOME/jre/lib查找 流程结束
类由启动类加载器完成加载
5. 中层回退尝试
扩展类加载器在JAVA_HOME/jre/lib/ext或java.ext.dirs查找 流程结束
类由扩展类加载器完成加载
6. 最终子加载器加载
回到最初接收请求的类加载器,在自身路径查找 流程结束
类由原加载器完成加载
抛出ClassNotFoundException异常
流程终止
五、典型场景案例:直观理解完整流程(加载用户自定义类)
以加载用户自定义类com.example.MyDemo (存储在项目classpath下,属于应用程序类加载器的加载范围)为例,结合默认类加载器层级,还原每一步具体执行过程,更易理解:
- 程序触发
com.example.MyDemo的加载,应用程序类加载器(系统默认加载器)接收到加载请求; - 应用程序类加载器不自行加载,将请求委派给父加载器 ------ 扩展类加载器;
- 扩展类加载器同样不自行加载,将请求委派给顶层父加载器 ------ 启动类加载器;
- 启动类加载器在自身路径(
JAVA_HOME/jre/lib)中查找com.example.MyDemo,未找到,加载失败,请求回退; - 加载请求回退到扩展类加载器,扩展类加载器在自身路径(
JAVA_HOME/jre/lib/ext)中查找该类,未找到,加载失败,请求继续回退; - 加载请求最终回退到应用程序类加载器,该加载器在自身路径(
java.class.path,即项目classpath)中找到该类,完成加载,流程结束。
补充:核心类加载的简化流程(如java.lang.String)
若加载的是 Java 核心类java.lang.String,流程会在第 4 步终止,无后续回退:
- 应用程序类加载器接收
String加载请求,依次委派给扩展类加载器、启动类加载器; - 启动类加载器在
JAVA_HOME/jre/lib/rt.jar(核心类库)中找到String类,直接完成加载,流程立即结束; - 扩展类加载器和应用程序类加载器均不会再参与任何加载操作,这也是双亲委派保证核心类安全的关键。
五、补充:父子加载器的关系
很多人会误以为类加载器的 "父子" 是继承(extends) 关系,实际是组合(composition) 关系:
子加载器内部持有父加载器的引用,通过该引用实现加载请求的向上委派;
所有自定义类加载器的最终父加载器都是应用程序类加载器,形成统一的委派链。
总结
- 双亲委派模型是 JVM 类加载的核心原则:请求向上委派,加载向下回退;
- 核心层级(自上而下):启动类加载器 → 扩展类加载器 → 应用程序类加载器;
- 核心价值:保证核心类安全(防止篡改) + 保证类唯一性(避免重复加载);
- 关键特性:父子加载器为组合关系(子持有父引用),非继承关系;
- 执行逻辑:父加载器优先尝试加载,仅当所有父加载器均加载失败时,子加载器才自行加载。