类加载器
# 类加载器
前言
对于任意一个类,必须由加载它的类加载器和类的本身共同一起确立在虚拟机中的为唯一性。两个类即使被同一个虚拟机加载,同一个Class文件,若加载器不同则这两个类不相等。本节内容主要了解关于类加载器相关知识,以及对双亲委派模型的理解。
# 一. 类加载的类型
根据《java虚拟机规范》提出: 严格的来讲类加载的类型分为以下两种:
# 1.1 引导类加载器(Bootstrap Class Loader)
- 此加载器主要加载放在<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的。同时被java虚拟机能够识别的类库(rt.jar,tools.jar)等的类库加载到虚拟机内存中。
- 这个类加载使用C++/C语言实现,嵌套虚拟机内部。
- 并没有继承 java.lang.ClassLoader,没有父加载器。
# 1.2 自定义加载器(User Class Loader)
- 《java虚拟机规范》提到将所有派生于抽象类ClassLoader的类加载器都划分为自定义加载器,典型的如增加除了磁盘位置之外的Class文件来源,或者通过类加载器实现类的隔离、重载等功能。其需扩展 java.lang.ClassLoader 类并且覆盖 findClass(String name) 方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。
# 1.3 对于扩展类加载(Extension Class Loader)和应用程序类加载器(Application Class Loader)理解
# 1.3.1 扩展类加载(Extension Class Loader)
- 由类sun.misc.Launcher$ExtClassLoader实现,java语言编写。
- 派生于ClassLoader类。
- 父类加载器为启动类加载器。
- 它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。
# 1.3.2 应用程序类加载器(Application Class Loader)
- 由类sun.misc.Launcher$AppClassLoader实现,java语言编写。
- 派生于ClassLoader类。
- 父类加载器为扩展类加载器。
- 加载属环境变量classpath或系统属性 java.class.path 指定路径下的类库
- 是程序中默认的类加载器,一般说,java应用的类都是由他完成加载

解答
其实 扩展类加载(Extension Class Loader)、应用程序类加载器(Application Class Loader) 这两个加载器也被算到了自定义加载器中,上述已经提到《java虚拟机规范》提到将所有派生于抽象类ClassLoader的类加载器都划分为自定义加载器。 如下图所示: 两个加载器都间接的继承ClassLoader类。


# 二. 双亲委派机制
# 2.1 工作原理
如果一个类加载器收到一个类的加载请求,它不会自己先去加载这个请求。
而是委托给它的父类加载器,每一层的加载器都是如此。因此所有的加载请求都应该委托到最顶层的
引导类加载器(Bootstrap Class Loader)。只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,
子加载器才会尝试自己去完成加载。如下图所示:
1
2
3
4
2
3
4

# 2.2 工作过程
- 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
- 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
- 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载。
- 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
# 2.3 源码实现
- 先检查请求加载的类型是否已经被加载过。
- 若没有则调用父加载器的loadClass()方法。
- 若父加载器为空则默认使用启动类加载器作为父加载器。
- 假如父类加载器加载失败抛出ClassNotFoundException异常的话,才调用自己的findClass()方法尝试进行加载。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 首先,检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if (c == null) {
try {
//若没有则调用父加载器的loadClass()方法。
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//若父加载器为空则默认使用启动类加载器作为父加载器。
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出ClassNotFoundException
// 说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类加载器无法加载时
// 再调用本身的findClass方法来进行类加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 2.4 双亲委派优势
- 避免类的重复加载。
- 保护程序安全,防止核心API被篡改。
编辑 (opens new window)
上次更新: 2023/01/05, 06:33:52