前置知识
类加载器
类加载器只有一个作用,就是负责把我们本地的.class字节码文件,加载到JVM中,以类模板的形式存在于JVM中。
类加载器加载的.class文件也是有要求的,并不是只要是后缀名为.class的文件,都可以被类加载器加载,这个.class文件的必须是.java文件经过编译后得到的字节码文件,也就是指这个.class文件的开头必须要是cafe babe字符单词,它的内容是经过加密后的二进制文件,但是此二进制使用十六进制表示出来的,如下图:
类加载器ClassLoader只负责加载.class文件,但至于他是否可以运行,由JVM中的执行引擎Excution Engine决定的。
Car.class是由.java文件编译得来的.class文件,存在本地磁盘。
ClassLoader:类加载器,负责加载并初始化 类文件,得到真正的Class类,即模板。
Car Class:由Car.class字节码文件,通过ClassLoader加载并且初始化得到,这个Car就是当前类的模板,这个Car Class模板就存在方法区。
car1,car2,car3:是由Car模板经过实例化而得,一个模板可以获得多个实例化对象。
拿car1举例,car1.getClass()可以得到模板Car类,Car.getClassLoader()可得到其装载器。
1. 双亲委派机制流程
当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
我的理解翻译:当java程序需要加载一个类的时候,比如要加载java.lang.String类,会首先去根加载器类BootstrapClassLoader的路径下去寻找,如果在根加载器路径下可以找到java.lang.String类,那么就不会再往下寻找java.lang.String类了,因为已经找到了;若在根加载器路径下没有找到java.lang.String类,则会去下一级扩展类加载器ExtensionClassLoader中寻找java.lang.String类,如果在扩展类加载器中找到了java.lang.String类,那么就不会再去下一级类加载器中寻找了;如果没有找到,则会去应用程序类加载器AppClassLoader中去寻找java.lang.String类,如果仍然没有找到会报classNotFoundException异常。
2. 类加载器
Java中提供如下四种类型的加载器,每一种加载器都有指定的加载对象,具体如下:
Bootstrap ClassLoader(启动类加载器) :主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
Extention ClassLoader(扩展类加载器):主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
Application ClassLoader(应用程序类加载器) :主要负责加载当前应用的classpath下的所有类
User ClassLoader(用户自定义类加载器) : 用户自定义的类加载器,可加载指定路径的class文件
2.1 类加载器测试
从上图可以看出启动类加载器BootStrapClassLoader是扩展类加载器ExtensionClassLoader的父级,扩展类加载器ExtensionClassLoader是应用程序加载器AppClassLoader的父级。
被某个类加载器加载的类,都会存放到这个类加载器的路径中,当程序需要加载某个类的时候,会先去根加载器BootstrapClassLoader的路径下,寻找是否有这个类的模板,如果没有则会去这个类加载器的下一级,若有,则不会再去下一级寻找了。
利用obj.getClass().getClassLoader()方法,可以找到对应的类模板是存放在哪个类加载器的路径下的,换一个说法,可以让你知道在加载这个类模板所对应的类的字节码文件的时候,是用的哪一个类加载器把这个类的字节码文件加载成类模板的,如下图:
那么问题来了?为什么说Object类是java自带的类呢?java自带的类到底可以在哪里找到呢?java自带的类可以JAVAHOME/jre/lib/rt.jar下 找 到 。 首 先 来 看 一 下 JAVAHOME/jre/lib/rt.jar下找到。首先来看一下JAVAHOME/jre/lib/rt.jar下找到。首先来看一下JAVAHOME,也即是java的安装目录,如下图:
JVM中的类加载器,启动类加载器BootStrap,在一开始,就会把rt.jar压缩包中的所有的 类的字节码文件都加载到JVM中,变成类的模板,所以这些java自带的类,对应的类的模板,都会被存放到根加载器BootStrap的路径下面。故当java程序中需要用到java中的自带类的时候,都会去启动类加载器BootStrap的路径下去寻找类的模板。
3. 双亲委派好处
- 通过委派的方式,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。
- 通过双亲委派的方式,还保证了安全性。因为Bootstrap ClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的,除非有人跑到你的机器上, 破坏你的JDK。那么,就可以避免有人自定义一个有破坏功能的java.lang.Integer被加载。这样可以有效的防止核心Java API被篡改。
https://www.jianshu.com/p/d4e4725b6d8d
https://blog.youkuaiyun.com/qq_45950109/article/details/116539405