Java中的类加载器和双亲委派机制
一,类加载器介绍
类加载器有三个:启动类加载器、扩展类加载器、应用程序加载器(系统加载器)
-
启动类加载器: null(不是由java实现的)启动类加载器加载java的核心类库,它负责将
jdk目录/jre/lib下面的类库或者jdk目录/jre/classes下的classes文件加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用.
所以不允许直接通过引用进行操作。
-
扩展类加载器:sun.misc.Launcher$ExtClassLoader扩展类加载器加载java扩展类库
它负责将
jdk目录/jre/lib/ext下的jar包或者jdk目录/jre/lib/ext/classes下的classes文件或者由系统变量 java.ext.dir 指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
-
应用类加载器: sun.misc.Launcher$AppClassLoader应用类加载器负责将系统类路径(CLASSPATH:指向当前目录./,如java代码在哪执行,哪里就是classpath)中指定的类库加载到内存中 ,可以通过getSystemClassLoader()获取 。
注意:idea的maven项目中classpath可以为resources资源目录也可以为java目录
开发者可以直接使用系统类加载器。
如果没有自己定义类加载,我们编写的Java类都将被这个类加载器加载。是程序中的默认类加载。
-
用户自定义类加载器用户自己编写的类加载器,可以通过覆盖findClass()方法去进行加载类。
二,类加载器的双亲委派机制
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGPtTioq-1576142906454)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212163811983.png)]](https://img-blog.csdnimg.cn/2019121217290151.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
如上图箭头所示,除了启动类加载器,每个类加载器都有一个父加载器。构成了一个类加载器结构系统。这个系统就是用来完成双亲委派机制的。
不同的类加载器加载的位置不一样,所以如果不同的位置有相同的类,那么会遵循双亲委派机制
工作过程:一个类加载器加载一个类时,首先会把加载动作委派给他的父加载器,如果父加载器无法完成这个加载动作时才由该类加载器进行加载。由于类加载器会向上传递加载请求,所以一个类加载时,首先尝试加载它的肯定是启动类加载器(逐级向上传递请求,直到启动类加载器,它没有父加载器),之后根据是否能加载的结果逐级让子类加载器尝试加载,直到加载成功。
比如我们自己写了一个com.test.T1类,假设我们没有自定义类加载器,那么这个类会由应用程序类加载器加载。应用程序类加载器加载时先把加载任务委派给扩展类加载器(父加载器),然后扩展类加载器再把加载任务委托给启动类加载器(父加载器),启动类加载器没有父加载器。于是它自己尝试加载,结果发现T1类并不在自己的加载类路径<Java_Runtime_Home>/lib之中,于是告诉扩展类加载器(子加载器)自己无法加载该类。这时扩展类只好自己加载这个类,结果发现也无法加载该类,于是它也告诉应用程序类加载器这个消息,这时应用程序类加载器自己进行T1类的加载动作,加载成功。
可以把每个类加载都想成一个大懒汉,每次让他办事时他都让爸爸代办。没想到爸爸也是个大懒汉,于是爸爸也让他的爸爸代办。这是到了爷爷那里,爷爷也很懒,但是他没有爸爸了,于是只能一边抱怨一边干,然后发现自己做不了,又骂骂咧咧的把活儿交给了自己的儿子,然后爸爸开始干活,发现自己也不能完成这个任务,于是他也是骂骂咧咧的把活交给了儿子,儿子挨了一顿骂,然后开始干活,经过了1小时的苦干,这个活儿终于完事了。
三,双亲委派机制的存在意义
双亲委派是Java语言的一大创新。表明看起来,由于双亲委派机制的存在,类加载器的数量增多了不少,增加了程序的复杂性。不过存在既有道理。双亲委派机制让Java类体系变得稳定,有层次性能。特定的类由特定的类加载器加载,每次加载都委托父类的过程让类对象在内存中的数量保持为一个,让同类名的类无法被替换。
如果没有双亲委派机制,只有一个类加载器,我们自己写一个java.lang.Object类,也可以被加载,结果就是内存中有两个Object类,引用的时候会造成安全的问题。而且一些核心的类可能会被替换,导致重大的安全问题。
有了双亲委派机制,我们自己写的java.lang.Object类在加载时会被加载器委托给父加载器,在某一个父加载器中发现内存中已经存在了java.lang.Object类,那么将不会进行加载,这样就保证了特定的类只能有一个在内存中。
四,双亲委派机制演示
-
首先在本地磁盘某个目录创建一个Test.java文件
public class Test{ public static void main(String[] args){ System.out.println("aaaaaa"); } }创建好后在cmd中进行编译
D:\>javac Test.java此时可以发现class文件已经出现
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVcKL0Vn-1576142906454)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212170044238.png)]](https://img-blog.csdnimg.cn/20191212172930515.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
-
将Test.class剪切到
jdk目录/jre/classes目录下(classes是自己建的目录)![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kh5laTKl-1576142906455)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212170342532.png)]](https://img-blog.csdnimg.cn/20191212172943473.png)
-
修改Test.java,并进行编译
public class Test{ public static void main(String[] args){ System.out.println("bbbbb"); } } -
再将编译好的Test.class文件剪切到
jdk目录/jre/lib/ext/classes目录下(classes是自己建的目录)![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c1OKVMdX-1576142906455)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212170729481.png)]](https://img-blog.csdnimg.cn/20191212173001577.png)
-
此时再次修改Test.java,并进行编译
public class Test{ public static void main(String[] args){ System.out.println("ccccc"); } }![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgCLXLKL-1576142906455)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212170824752.png)]](https://img-blog.csdnimg.cn/20191212173020213.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
-
此时运行此程序,观察结果
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TfnAKrty-1576142906455)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212170940253.png)]](https://img-blog.csdnimg.cn/20191212173046337.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
可以看到显示结果为aaaaa,表示委派给了启动类加载器进行加载
-
此时删除启动类加载器的目录
jdk目录/jre/classes下的class文件,再次执行程序![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1w8j3kxw-1576142906456)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212171710133.png)]](https://img-blog.csdnimg.cn/20191212173122475.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXrE2W12-1576142906456)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212171741452.png)]](https://img-blog.csdnimg.cn/20191212173145416.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
可以看到结果为bbbbb,表示如果启动类加载器加载不了,则会返回结果null(因为启动类加载器不是用java写的,抛不了异常),让启动类加载器的子加载器(扩展类加载器)来加载
-
此时删除扩展类加载器的目录
jdk目录/jre/lib/ext/classes下的classes文件,再次执行程序![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qd9LYM10-1576142906456)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212172135557.png)]](https://img-blog.csdnimg.cn/20191212173158398.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LviDQFYc-1576142906457)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212172150938.png)]](https://img-blog.csdnimg.cn/2019121217321022.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI2ODM4MzE1,size_16,color_FFFFFF,t_70)
可以看到结果为ccccc,表示如果启动类加载器的子加载器(扩展类加载器)也加载不了,则会抛出异常给扩展类的子加载器(应用类加载器),并委派给应用类加载器来进行类的加载

浙公网安备 33010602011771号