什么是java类加载器?其实Java类加载器是Java运行时环境的一部分,负责动态加载Java类到Java虚拟机的内存空间中。
Java类加载器的工作原理
一说Java代码的执行,接触过Java代码的同学肯定会想到: 编写、编译、运行这三个阶段。编写是在后缀名为.java的文件中, 根据Java语法规则编写源代码;编译是将。java文件(源代码文件)编译成.class文件(字节码文件);运行是通过JVM来执行.class字节码文件。
大白话解释: .Java文件是程序员能看懂,但是计算机看不懂的文件。需要先把它转换成.class文件,计算机才能识别,从而来执行。虽然这三个阶段都可以通过IDE实现,但是好多人容易忽略一个细节,那就是: 在JVM执行。class字节码文件之前, 需要先通过”类加载器”将该字节码文件加载到内存中,而这个过程,就是我们要详聊的话题。
下面我们从类加载器的概述、类加载器的分类、类加载机制三个方面来介绍java类加载器。
1、java类加载器的概述
类加载器(ClassLoader)是负责加载类的对象的,也就是将.class字节码文件加载到JVM内存中的。那它什么时候才会去加载.class字节码文件呢? 答案是: 当Java程序第一次使用某个类中的内容,而该类的字节码文件在内存中不存在时,类加载器就会去加载该类的字节码文件。
俗话说”渡人先渡己”,要想成为别人的榜样,帮助别人。首先要做好自己。生活中如此, 类加载器也一样。要想加载我们自定义的类,类加载器必须先完成”自加载”的过程。聊到这,不得不提的就是”类加载器的分类”了。
2、java类加载器的分类
Java中的类加载器主要分为以下四类:
(1)根类加载器(BootStrapClassLoader), 主要负责加载jre/lib/rt.jar相关的字节码文件的。
(2)扩展类加载器(ExtensionClassLoader), 主要负载加载 jre/lib/ext/*.jar 这些jar包的。 该类加载器在JDK1。9的时候更名为: Platform Class Loader, 其父类加载器为: null。
(3)应用程序类加载器(ApplicationClassLoader), 主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。 该类加载器在JDK1.9的时候更名为: System ClassLoader, 其父类加载器为: ExtensionClassLoader。
(4)自定义类加载器(UserClassLoader), 负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。
如下图:
到这,相信大家对类加载器已经初步有一定的认识和理解了。接下来,我们写代码来验证一下,代码和打印结果如下:
到这里,代码就已经验证完毕了。其实我们现在一直在研究的是JVM类加载机制的"加载循序", 现在,我们来研究下它的"检查顺序",请你思考,假设: D:\compile、ext\*.jar、rt.jar三类中都有A.class,那么A.class是否会被加载3次,如果不会,它的加载顺序是什么样的?
答案是: 不会被加载3次,并最终会由BootStrapClassLoader来加载A.class。
原因是因为,APPClassLoader类加载器(以下简称: app)加载之前,会先询问ExtClassLoader类加载器(以下简称: ext)是否加载。如果ext加载,app就不加载了,反之则app加载。同样, ext在加载之前, 也会询问BootStrapClassLoader类加载器(以下简称: bootstrap)是否加载, 如果bootstrap加载,则ext就不加载了,反之,则ext加载。这也是: JVM类加载机制的”双亲委派机制”。
2、java类加载器的类加载机制
最后,我们再来聊一聊”类加载机制”,在JVM中类加载机制主要有3种:
(1)全盘加载。顾名思义,就是当某一个类加载器加载某个.class文件时, 默认也会连同该文件所依赖的.class一起加载(除非显示声明通过某个指定的类加载器加载)。
(2) 缓存机制。即所有类加载器已经加载过的.class文件都会被保存到缓存中,下次使用该.class文件时,JVM会优先从缓存中查找,如果没有,才会去加载指定的字节码文件,这也是为什么当字节码文件变化后, 需要重启JVM后才能看到修改效果的原因。
(3)双亲委派。大白话解释,儿子(App)要星星,他自己实现不了,就找他老爹(Ext)要,他老爹能实现的话就给他了,实现不了,就找他爷爷(BootStrap)要,说: 你孙子要天上的星星。他爷爷如果能实现就给了,如果也实现不了,就会告诉他爹(Ext),让你儿子(App)自己实现吧。这种情况有点极端,属于谁都没有加载,则程序报错,会抛出异常。
(4)总结: 类加载器自上而下检查(App --> Ext --> BootStrap),自下而上加载(BootStrap --> Ext --> App)。