了解类加载器吗?类加载器的类型有哪些?
一、什么是类加载器(ClassLoader)
类加载器是 Java 虚拟机中的一部分,负责将 .class
文件加载到 JVM 内存中,生成对应的 Class
对象。
Java 程序中所有的类在使用前都必须通过类加载器加载进 JVM,才能被执行。
二、类加载器的作用
- 加载
.class
文件到内存中。 - 将字节码转换为 JVM 能识别的
Class
对象。 - 实现类的 命名空间隔离。
- 支持 模块化开发(如插件机制、自定义业务模块加载)。
三、类加载器的分类(JVM 内置 + 用户自定义)
1. 启动类加载器(Bootstrap ClassLoader)
- 作用: 加载 JVM 的核心类库,如
java.lang.*
、java.util.*
等。 - 加载路径:
JAVA_HOME/lib
目录中的类(如rt.jar
)。 - 实现: 由 C/C++ 实现,是 JVM 的一部分。
- 特点: 不是 Java 类,不能被直接引用或操作。
2. 扩展类加载器(Extension ClassLoader)
- 作用: 加载 Java 扩展类库。
- 加载路径:
JAVA_HOME/lib/ext/
目录或由java.ext.dirs
系统变量指定的路径。 - 父加载器: Bootstrap ClassLoader。
- 类名:
sun.misc.Launcher$ExtClassLoader
。
3. 应用类加载器 / 系统类加载器(Application ClassLoader)
- 作用: 加载用户类路径(classpath)下的类文件。
- 加载路径: 当前应用的
classpath
(如 jar 包或类文件所在目录)。 - 父加载器: Extension ClassLoader。
- 类名:
sun.misc.Launcher$AppClassLoader
。
4. 自定义类加载器(Custom ClassLoader)
-
作用: 开发者可以继承
java.lang.ClassLoader
实现自己的加载逻辑。 -
使用场景:
- 热部署
- 模块化(如 OSGi)
- 插件系统
- 加密 class 文件
-
常见方式:
- 继承
ClassLoader
并重写findClass()
方法 - 调用
defineClass()
定义类对象
- 继承
四、类加载器的层次结构图
┌──────────────────────────┐
│ Bootstrap ClassLoader │
│ (C++实现, 加载核心类库) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ Extension ClassLoader │
│ (加载 ext 目录类) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ Application ClassLoader │
│ (加载classpath下类) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ 自定义 ClassLoader │
│ (可指定加载路径/策略) │
└──────────────────────────┘
五、双亲委派机制(Parent Delegation Model)
定义:
类加载器在加载类时,首先会 将加载请求委托给父加载器,由顶层的 Bootstrap 开始查找,只有在父加载器找不到时,才由当前加载器加载。
加载流程:
- 当前类加载器收到类加载请求。
- 委托给父类加载器。
- 如果父类无法加载,才由当前加载器尝试加载。
优点:
- 避免类的重复加载。
- 防止用户自定义类覆盖 JDK 核心类(如
java.lang.String
)。
举例说明:
public class Test {public static void main(String[] args) {System.out.println(String.class.getClassLoader()); // null(Bootstrap)System.out.println(Test.class.getClassLoader()); // AppClassLoader}
}
六、自定义类加载器示例
public class MyClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] data = loadClassData(name); // 从文件或网络中读取字节数组return defineClass(name, data, 0, data.length);}
}
七、线程上下文类加载器(ContextClassLoader)
定义:
- 每个线程可以设置自己的类加载器,用于动态加载类或资源。
- 默认是
Application ClassLoader
。
用途:
- 在 Java SPI(Service Provider Interface)中尤为重要。
- 解决双亲委派带来的灵活性限制。
Thread.currentThread().setContextClassLoader(new MyClassLoader());
八、类加载器相关方法(Java API)
方法 | 说明 |
---|---|
loadClass(String name) | 加载类(会委托给父类) |
findClass(String name) | 查找类(自定义类加载核心) |
defineClass(...) | 将字节数组转为 Class 对象 |
getParent() | 获取父加载器 |
getClassLoader() | 获取当前类的加载器 |
九、面试常问点总结
问题 | 要点回答 |
---|---|
什么是类加载器? | 将 .class 加载进内存,生成 Class 对象。 |
JVM 有哪些类加载器? | 启动类、扩展类、应用类、自定义类加载器。 |
双亲委派模型是什么? | 加载委托给父加载器,避免重复 & 保证安全性。 |
如何打破双亲委派? | 重写 loadClass() 不委托父类。 |
自定义类加载器的用途? | 插件、加密、安全、热更新、动态部署等。 |