了解完类的生命周期前置知识 第一阶段就是加载 而这个过程就少不了类加载器
类加载器收到类和接口字节码这些二进制文件 会调用JNI接口 也就是java虚拟机中的方法 在之前讲的方法区和堆区对象的创建 保存刚接收到的类和接口的信息
1.类加载器 的分类
jdk8以前核心的加载器就是这 三个 现在逐一认识
启动类加载器:
在程序运行当中 像rt.jar这个jar包的核心类字符串类 已经被加载到了虚拟机中 这个加载过程就是由启动器加载类完成的 但是出于安全考虑java虚拟机没办法让程序员获取到这个启动类加载器 那么如果要对JDK进行扩展 添加一些比较基础的核心类 那么如何让启动器加载器帮我们去加载这些扩展的类呢
扩展类加载器和应用程序类加载器 :
扩展类加载器就是加载通用但不核心的类 而应用程序加载器既可以加载自己项目中的类也可以加载maven(第三方)依赖中包含的一些类
面试体验:
但有的时候我们得要去打破这种双亲委派机制
01---自定义类加载器
首先是为什么要打破委派机制的情景:
解决方法:
那么如何自定义类加载器并实现委派机制呢?
先来看
那么在方法中将打破双亲委派机制代码重新实现就可以了
进入源码:
在双亲委派机制核心代码中 它通过向上的委派 一层一层的去判断 咱们这个类有没有由他父亲加载器加载过 如果都没有加载成功 那么就会由本类加载器进行加载 这样就实现了整个双亲委派机制的逻辑
ps: 一个类如果由两个类加载器分别进行加载 那么在程序中其实是有两个不同的类会出现
02-----线程上下文的类加载器 (以JDBC为例
JDBC这个框架是用来java操作数据库的 但是为了提高泛用性 不想出现某个数据库的语法 将来对接每个数据库都非常容易 因此:
而在去加载驱动jar包的类的过程中就打破了双亲委派机制 为什么:
DriverManager是jdk自己提供的 位于rt.jar包中 所以会由启动类加载器进行加载 而它最终要加载咱们引入的jar包中对应的驱动类 而咱们引入的jar包中的驱动类却由应用程序类加载器加载 所以产生了冲突 :也就是启动类加载器加载完driverManager后 这个事需要委派给应用程序类加载器加载jar包的mysql驱动
那么现在就想弄清 driverManager 是如何实现的
首先解决个问题:driverManager位于rt.jar包 也就是jdk的底层 那么它是如何知道jar包中加载的驱动在哪 答案用了jdk底层的一种技术
所以:
需要通过SPI机制发现驱动,并通过上下文加载器(AppClassloader)进行加载对应的驱动
(类A引用类B,会使用类A的类加载器去加载类B,现在类A的类加载器不支持加载类B,使用上下文切换到类B的加载器去加载类B)
线程上下文的类加载器-------jdbc案例的小结:
jdbc最核心的类是driverManager位于rt.jar包中 所以由启动类加载器加载 而它要拿到第三方jar包中对应的数据库驱动 所以底层运用了SPI机制来找到对应jar包中的驱动 但是driverManager要拿到应用程序类加载器才能去加载 所以使用了线程上下文类的加载器 也就是说一个类的线程创建完之后 虚拟机底层会将这个应用程序类加载器 放到这个线程上下文中 这样在任何地方都可以取到它 所以在SPI机制中也是利用这种方式去获取到应用程序类加载器 并且加载到了jar包中驱动
ps: 其实真正要打破双亲委派机制是要去重新LoadClass方法
03----OSGI框架的类加载器 (了解即可)
04---jdk9之后的加载器
jdk8就是按照jar包的位置去加载字节码文件 而到jdk9后 类不存在在jar包中 而是会把java的类放到叫jmod文件中 所以类加载器就要从原来的jar包中进行加载转变成从这些jmod的文件中进行加载
至此 类加载器知识结束