前言
- 从 Java 虚拟机角度看,存在 启动类加载器 (Boostrap Class Loader) 和 其他所有的类加载器 两种类加载器。前一种是 C++ 实现的,是虚拟机自身的一部分;后一种是 Java 实现的,独立于虚拟机外部。
三层类加载器
- 启动类加载器 (Bootstrap Class Loader)
- 负责加载存放在 <JAVA_HOME>\lib 目录,或者被 -Xbootclasspath 参数所指定的路径存放,是 Java 虚拟机能够识别的类库加载到虚拟机内存中。
- 拓展类加载器 (Extention Class Loader)
- 负责加载 <JAVA_HOME>\lib\ext 目录,或者被 java.ext.dirs 系统变量所指定的路径中所有的类库。它是在 sun.misc.Launcher$ExtClassLoader 中以 Java 代码的形式实现的。
- 应用程序类加载器 (Application Class Loader)
- 负责加载用户类路径 (ClassPath) 上的所有类库,开发者可以直接在代码中使用这个类。它是由 sun.misc.Lauuncher$AppClassLoader 来实现。在一些场合也被称为“系统类加载器”。
双亲委派模型 & 工作过程
- 双亲委派图示。

- 双亲委派工作过程:如果一个类加载器收到类加载的请求,它会首先把请求派给父类加载器去完成,每一个层次都是如此,因此所有的加载请求最终都送到了启动类加载器中,只有当父加载器反馈无法完成时,子加载器才会尝试自己加载。
- 使用双亲委派的好处是 Java 的类随着它的类加载器一起具备了一种优先级层次的关系。例如 java.lang.Object,存放在 rt.jar 中,无论哪一个类要加载这个类,最终都会委派给启动类加载器加载。如果没有采用双亲委派模型,由各自类加载器去加载,那么最后在系统中就会多个不同的 Object 类,从而造成混乱。示例代码见下。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
// 检查请求是否已经被加载过
Class c = findLoadClass()name
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name,false)
} else {
c = findBootstrapClassOrNull(name)
}
} catch (ClassNotFoundException e) {
// 如果父类加载器抛出异常,则说明父类加载器无法完成加载请求
}
if (c == null) {
// 在父类中无法加载,再调用本身的 findClass 方法加载
c = findClass(name)
}
}
if (resolve == true) {
resolveClass(c)
}
return c
}
- 由上面代码可看出,首先先检查请求的类型是否已经被加载过,若没有则调用父类加载器的 loadClass() 方法,若父类加载器为空则默认使用启动类加载器作为父加载器。如果父加载器加载失败的话,会抛出 ClassNotFoundException 异常,才会调用自己的findClass() 方法尝试加载。