双亲委派模型

JVM

类加载器

从java虚拟机的角度来说,只存在两种不同的类加载器,一种是根类加载器,这个类加载器使用c++代码实现的,是java虚拟机的一部分;另一种是所有其他的类加载器,这些类加载器都是用java语言实现的,独立于java虚拟机之外的,并且全都继承自java.lang.ClassLoader。

从java开发人员的角度来说,加载器可以划分的更细致些,分为下面所说的三种类加载器。

java虚拟机可以安装多个类加载器,默认有三个主要的类加载器 BootStrap,ExtClassLoader,AppClassLoader.

类加载器之间的父子关系
BootStrap——->ExtClassLoader——->AppClassLoader (爷爷——->儿子——->孙子)
根类加载器 扩展类加载器 系统类加载器也叫应用类加载器

BootStrap,ExtClassLoader,AppClassLoader分别负责加载不同路径下的class文件

BootStrap ———Java/jdk/jre/lib/rt.jar

ExtClassLoader ———Java/jdk/jre/lib/ext/*jar

AppClassLoader ———classpath指定的所有jar或者目录

此处输入图片的描述

BootStrap 根类加载器 负责加载Java/jdk/jre/lib/路径下的java的核心类。只能加载这个路径下能被虚拟机识别的类库(仅按照文件名进行识别,如rt.jar,名字不符合的类库即使放在这个目录下面也不会被加载)。
以下代码,可以获得根类加载器所加载的核心类库

1
2
3
4
5
6
7
8
// 获取根类加载器所加载的全部URL数组
URL[] urls = sun.misc.Launcher.
getBootstrapClassPath().getURLs();
// 遍历、输出根类加载器加载的全部URL
for (int i = 0; i < urls.length; i++)
{
System.out.println(urls[i].toExternalForm());
}

ExtClassLoader 扩展类加载器,负责加载 Java/jdk/jre/lib/ext/*jar 路径下的类,通过这种方式,就可以为java扩展核心类以外的新功能,将我们自己开发的类打包成jar文件,然后放在 Java/jdk/jre/lib/ext/目录下,就可使用我们自己开发的类

AppClassLoader 系统类加载器,也叫应用类加载器,负责加载classpath指定的所有jar或者目录,程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都将以系统类加载器作为父类加载器。

双亲委派模型

原理

双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码,类加载器间的关系如下:

此处输入图片的描述

其工作过程是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成。

使用双亲委派模型来组织类加载器的关系,很好的解决了类加载器的基础类的统一问题(越基础的类由越上层的类加载器加载。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。(即使自定义了自己的类加载器,强行用defineClass()方法去加载一个以“java.lang”开头的类,也不会成功。如果尝试这样做的话,将会收到一个由虚拟机自己抛出的异常。)

实现双亲委派模型的代码都集中在java.lang.ClassLoader的loadClass方法之中,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected synchronized Class<?> loadClass(String name,boolean resolve)throws ClassNotFoundException{
//check the class has been loaded or not
Class c = findLoadedClass(name);
if(c == null){
try{
if(parent != null){
c = parent.loadClass(name,false);
}else{
c = findBootstrapClassOrNull(name);
}
}catch(ClassNotFoundException e){
//if throws the exception ,the father can not complete the load
}
if(c == null){
c = findClass(name);
}
}
if(resolve){
resolveClass(c);
}
return c;
}

在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器