类加载器
类加载器有四种
- 启动类加载器(Bootstrap ClassLoader)
负责加载 JAVA_HOMElib ⽬录中的,或通过-Xbootclasspath参数指定路径中的且被虚拟机认可(按⽂件名识别,如rt.jar)的类。这个加载器由C++实现的,并不是java实现的
- 扩展类加载器(Extension ClassLoader)
负责加载 JAVA_HOMElibext ⽬录中的,或通过java.ext.dirs系统变量指定路径中的类库。
- 应⽤程序类加载器(Application ClassLoader)
负责加载⽤户路径(classpath)上的类库
- 自定义加载器(User ClassLoader)
JVM⾃带的三个加载器只能加载指定路径下的类字节码,如果某个情况下,我们需要加载应⽤程序之外的类⽂件呢?⽐如本地D盘下的,或者去加载⽹络上的某个类⽂件,这种情况就可以使⽤⾃定义加载器了。
自定义加载器的步骤,(1)继承ClassLoader ——>(2)重写findClass()⽅法 ——>(3)调⽤defineClass()⽅法

加载器
JVM的类加载器是通过ClassLoader及其⼦类来完成的,类的层次关系和加载顺序可以由下图来描述

加载过程中会先检查类是否被已加载,检查顺序是⾃底向上,从Custom ClassLoader到BootStrapClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载⼀次。⽽加载的顺序是⾃顶向下,也就是由上层来逐层尝试加载此类。
双亲委派模型
JVM通过双亲委派模型进⾏类的加载,当⼀个类加载器收到类加载任务,会先交给其⽗类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当⽗类加载器⽆法完成加载任务时,才会尝试执⾏加载任务。
这样可以避免重复加载,当⽗亲已经加载了该类的时候,就没有必要⼦ClassLoader再加载⼀次。
我们可以想一下,若是不这么做,那我们就可以随时使⽤⾃定义的String来动态替代java核⼼api中定义的类型,这样会存在⾮常⼤的安全隐患,⽽双亲委托的⽅式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以⽤户⾃定义的ClassLoader永远也⽆法加载⼀个⾃⼰写的String,除⾮你改变JDK中ClassLoader搜索类的默认算法。
破坏双亲委派模型

在某些情况下⽗类加载器需要加载的class⽂件由于受到加载范围的限制,⽗类加载器⽆法加载到需要的⽂件,这个时候就需要委托⼦类加载器进⾏加载。
但是按照双亲委派模式,⼦类需要委托⽗类加载器去加载class⽂件。无疑是没法达到我们的目的地。这个时候就需要破坏双亲委派模式才能加载成功⽗类加载器需要的类。就需要⽗类会委托⼦类去加载它需要的class⽂件。
比如 jdk 中定义的 Driver 接⼝,但它的实现由在各大数据库的服务商来提供,像mysql的就写了MySQL Connector ,这些实现类都是以jar包的形式放到classpath⽬录下。
so,问题就来了,DriverManager(也由jdk提供)要加载各个实现了Driver接⼝的实现类(classpath下),然后进⾏管理,但是DriverManager由启动类加载器加载,只能加载JAVA_HOME的lib下⽂件,⽽其实现是由服务商提供的,在classpath目录下,由系统类加载器加载,这个时候就需要启动类加载器来委托⼦类来加载Driver实现,从⽽破坏了双亲委派。这⾥仅仅是举了破坏双亲委派的其中⼀个情况。
大家还有其他破坏双亲委派模型的例子不,可以留言分享一下。
个人学习整理,有错欢迎纠正