![034404b6d30ef6ac1095fdf139899762.png](https://i-blog.csdnimg.cn/blog_migrate/219c6e877f2bfa7d709778c3fd49e064.jpeg)
跬步千里 类加载机制
左右逢猿 左右逢猿 今天
上一篇我们说了静态代理,本来想直接去说动态代理,但是从我自身出发,脱离了类加载机制去说动态代理,就好像是隔靴搔痒,不得精髓,所以今天先说一下java的类加载机制
现在我们先想一下,我们写一个*.java文件,这个文件是怎么一步步的成为一个在jvm中供我们调用的类的呢,其实这个把*.java文件变为jvm中一个类的过程,就被称为java的类加载
java中系统自带的类加载器有三种:
1.BootStrapClassLoader 这个类加载器是专门帮我们加载%JAVA_HOME%/lib下的jar包
![892b773d33fc5df2b46a2379e683e34e.png](https://i-blog.csdnimg.cn/blog_migrate/37d8ad74c878f3da6f92ae688cb5e4ef.jpeg)
也就是这些,其中比较重要的比如rt.jar,如果我们引用了一个String类,这个时候BootStrap ClassLoader就会加载并装备一个String对象到内存中
2.Extention ClassLoader 这个加载器也是jvm自带的加载器,负责加载%JAVA_HOME%/lib/ext文件夹下的jar包
![19daf63aa896babe3c53976b939d6a05.png](https://i-blog.csdnimg.cn/blog_migrate/f62a516c6fa5b597d32b6247eff5aa25.jpeg)
道理跟前面一样,当用到该目录下面jar包中的类时,就会由这个ClassLoader装配一个类到jvm中
3.Appclass Loader 这个classLoader负责加载classPath目录下的类,当我们自己编写一个类,不指定classLoader的时候默认使用该加载器进行类加载
我们看一下ClassLoader的结构
![838eeec5b9f1ac2e33a64d119f359378.png](https://i-blog.csdnimg.cn/blog_migrate/bfe9005f400695507740343fcff7b302.jpeg)
我们会发现它有一个属性parent,说明ClassLoader之间是有层级关系的,我们现在看一下这个层级关系,首先测试一下我们正常在ClassPath下的类用的是哪种类加载器
![f11798444abd2b66aed4d5119d98688e.png](https://i-blog.csdnimg.cn/blog_migrate/5df8f007146d5bba88f0cb3451381e47.jpeg)
验证了我们的想法,在不指定类加载器的情况下,使用的是AppClassLoader,如果我们尝试去获取它的父加载器,会是什么呢
![b1656f851521976cbc52cc0dbc069a0f.png](https://i-blog.csdnimg.cn/blog_migrate/51d4cab7bd18f4948bb256da062076e8.jpeg)
我们发现居然是负责加载java_hoem/lib/ext下面jar包的ExtClassLoader,如果我们继续获取父类呢
![95e5f9ed725f4a321c3987e147a94e2f.png](https://i-blog.csdnimg.cn/blog_migrate/282bdd0ac58914d6c62ebda49b60942c.jpeg)
我们发现是null,这是不是代表继承的层级关系结束了呢,其实不是,null代表的正是我们上面说的BootStrapClassLoader,我们称之为根加载器,是C语言实现的,所以就此我们就得出了结论,也就是我们在类加载中常说的“双亲委派模型”,双亲指的就是ExtClassLoader和BootStrapClassLoader
梳理一下当我们尝试加载一个类的时候会发生什么,首先有一点要明白,C;assLoader在加载类的同时会维护一个已加载类的集合,参照源码
![e051d4c2ec607d9f369f7ae56836fabe.png](https://i-blog.csdnimg.cn/blog_migrate/2f69a135cbfac717ae3660627a00c6fc.jpeg)
下面正式说明双亲委派模型的原理
- 首先在当前类的类加载器中查找是否已经加载了该类,如果没有特别指定,当前加载的自定义类使用的就是AppClassLoader,如果已经加载了该类,直接返回该类型到JVM方法区,进行链接过程(之后会有说明),如果当前类加载器中没有,则会继续到父类中查找
- AppClassLoader的父类也就是ExtClassLoader,同理如果有则加载完成,没有继续向上,交给BootStrapClassLoader进行查找
所以我们常说的双亲委派模型,是在我们没有指定自定义的类加载器情况下的说法,如果我们自己实现了类加载器,并且指定父加载器为AppClassLoder,那么双亲委派就会变成三亲委派
既然提到了ClassLoader会持有加载类的一份引用,那我们就顺便看一下Class类有没有持有ClassLoader的相关信息呢
![7d5945fa3b9c40f6ec29dd35f62e7a29.png](https://i-blog.csdnimg.cn/blog_migrate/112d86b8d06622e0476e9db4ab0f2385.jpeg)
答案是肯定的,那么现在问题来了,由不同的类加载器加载的同一类型的类,最终得到的类相同吗,也就是 互相会instanceOf吗?
答案显而易见,判断两个类是否是同一种类型,不仅要看是否是同一个全类名,还要看是否是同一个类加载器加载的,有时候我们也可以利用这一点来解决同一个类需要同时维护不同版本的问题,也就是我们项目中的“钻石依赖”问题
我们知道,一个*.java文件最终变成jvm堆中一个可以供我们使用的对象,需要经历以下阶段
- 加载,查找java文件转换成为classe二进制字节码,主要是通过重写ClassLoader中的findClass方法完成
- 链接,链接是个大的过程,中间又分为验证,准备和解析
- 初始化
经历了这些才最终变为一个可以使用的java对象,今天只是重点说了一下第一部分,类的加载,为了避免文章过于冗长,后面两部分在下两篇文章再具体展开~