Java 类加载器与双亲委派模型
·
一、核心概念总览
1. 什么是类加载器(ClassLoader)
定义:Java 虚拟机(JVM)中用于加载 .class 字节码文件到内存中,并生成对应的 java.lang.Class 对象的组件。核心作用:
- 读取本地 / 网络 / 数据库等任意来源的字节码文件
- 确保类的唯一性、安全性
- 实现类的热部署、模块化加载等高级功能
2. 类加载的生命周期(简版)
加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载类加载器只负责「加载」阶段,后续步骤由 JVM 自动完成。
二、Java 内置三种类加载器
JVM 自带三层类加载器,层级结构从上到下:
1. 启动类加载器(Bootstrap ClassLoader)
- 最顶层,由 C++ 实现,无对应 Java 对象
- 加载核心类库:
$JAVA_HOME/jre/lib下的核心包(rt.jar、resources.jar 等) - 加载
java.lang.*、java.util.*等基础类 - 无法被 Java 代码直接获取(获取结果为
null)
2. 扩展类加载器(Extension ClassLoader)
- 由 Java 实现(
sun.misc.Launcher$ExtClassLoader) - 加载扩展类库:
$JAVA_HOME/jre/lib/ext目录下的 jar 包 - 父加载器:启动类加载器
3. 应用程序类加载器(App ClassLoader)
- 默认系统类加载器(
sun.misc.Launcher$AppClassLoader) - 加载项目 classpath 下的所有类(我们写的业务类、第三方 jar 包)
- 父加载器:扩展类加载器
- 可通过
ClassLoader.getSystemClassLoader()获取
层级关系图
plaintext
启动类加载器(Bootstrap)
↑
扩展类加载器(Extension)
↑
应用程序类加载器(App)
↑
自定义类加载器(Custom)
三、双亲委派模型(核心原理)
1. 定义
当一个类加载器收到类加载请求时:
- 自己不先加载,把请求向上委托给父类加载器
- 所有加载器依次向上委托,直到启动类加载器
- 若父加载器无法加载(找不到类),才由子加载器尝试加载
一句话总结:向上委托,向下加载。
2. 核心源码(ClassLoader.loadClass())
java
运行
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. 检查类是否已经加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
// 2. 委托给父类加载器加载(核心!)
c = parent.loadClass(name, false);
} else {
// 3. 父类为空,使用启动类加载器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 父类加载失败,自己加载
}
if (c == null) {
// 4. 父类无法加载,自己调用 findClass 加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
3. 双亲委派模型的三大优势
- 避免类重复加载保证一个类在 JVM 中只存在一个 Class 对象
- 保证核心类安全防止恶意代码替换 Java 核心类(如自定义
java.lang.String无法被加载) - 层级清晰、职责分离核心类、扩展类、业务类分层加载,便于管理
四、破坏双亲委派模型(面试高频)
1. 什么是破坏?
不遵循「向上委托」规则,类加载器直接自己加载类,绕过父类。
2. 为什么要破坏?
- 热部署(热更)
- 模块化隔离(SPI、OSGi、Spring Boot)
- 不同模块需要加载同名不同版本的类
3. 常见破坏场景
- 重写
loadClass()方法(不推荐) - SPI 机制(JDBC、JNDI)
- Tomcat 类加载器
- OSGi 模块化框架
五、实战:自定义类加载器
1. 实现步骤
- 继承
java.lang.ClassLoader - 重写
findClass()方法(遵守双亲委派) - 读取字节码文件 → 调用
defineClass()生成 Class 对象
2. 完整代码
java
运行
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
/**
* 自定义文件系统类加载器
* 遵守双亲委派模型
*/
public class CustomClassLoader extends ClassLoader {
// 类文件所在路径
private final String classPath;
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
/**
* 重写 findClass 方法(核心)
*/
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
try {
// 1. 读取 .class 文件字节码
byte[] classData = loadClassData(className);
// 2. 将字节码转为 Class 对象(JVM native 方法)
return defineClass(className, classData, 0, classData.length);
} catch (Exception e) {
throw new ClassNotFoundException("类加载失败:" + className, e);
}
}
/**
* 从文件系统读取字节码
*/
private byte[] loadClassData(String className) throws Exception {
// 类名转文件路径:com.test.User → com/test/User.class
String path = classPath + "/" + className.replace(".", "/") + ".class";
try (FileInputStream fis = new FileInputStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
}
}
// 测试
public static void main(String[] args) throws Exception {
// 指定 class 文件目录
CustomClassLoader loader = new CustomClassLoader("D:/project/target/classes");
// 加载类
Class<?> clazz = loader.loadClass("com.example.demo.TestClass");
// 输出类加载器
System.out.println("类加载器:" + clazz.getClassLoader());
System.out.println("父加载器:" + clazz.getClassLoader().getParent());
}
}
3. 关键方法说明
表格
| 方法 | 作用 |
|---|---|
loadClass() |
实现双亲委派逻辑,不要轻易重写 |
findClass() |
自定义加载逻辑,推荐重写 |
defineClass() |
将字节码转为 Class 对象,native 方法 |
六、面试高频考点(必背)
1. 三种类加载器及其职责?
- 启动类加载器:加载 JRE 核心类
- 扩展类加载器:加载 ext 目录扩展类
- 应用类加载器:加载项目 classpath 类
2. 双亲委派模型流程?
收到请求 → 向上委托 → 顶层无法加载 → 向下加载。
3. 双亲委派的好处?
防重复加载、保证核心类安全、职责清晰。
4. 如何自定义类加载器?
继承 ClassLoader,重写 findClass(),调用 defineClass()。
5. 哪些场景破坏了双亲委派?
SPI、Tomcat、OSGi、热部署。
6. 为什么 Tomcat 要破坏?
一个服务器运行多个 Web 应用,不同应用可使用不同版本的第三方包,需要隔离。
七、总结
- 类加载器:负责把
.class加载进内存生成Class对象 - 三层结构:启动 → 扩展 → 应用 → 自定义
- 双亲委派:向上委托,向下加载,保证安全与唯一性
- 自定义加载器:重写
findClass(),遵守模型更安全 - 破坏模型:为了隔离、热更、模块化,是高级应用场景
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)