写在前面
本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和文献引用请见100个问题搞定Java虚拟机
解答
当类加载器(Class-Loader)试图加载某个类型的时候,除非父加载器找不到相应类型,否则尽量将这个任务代理给当前加载器的父加载器去做。
使用委派模型的目的
1. 避免重复加载
2. 保障加载的安全性
补充
类加载器
启动类加载器(Bootstrap Class-Loader)
加载 jre/lib 下面的 jar 文件,如 rt.jar。
它是个超级公民,即使是在开启了 Security Manager 的时候,JDK 仍赋予了它加载的程序 AllPermission。
我们一般可以使用下面方法获取父加载器,但是在通常的 JDK/JRE 实现中,扩展类加载器 getParent() 都只能返回 null。
public final ClassLoader getParent()
扩展类加载器(Extension or Ext Class-Loader)
负责加载我们放到 jre/lib/ext/ 目录下面的 jar 包,这就是所谓的 extension 机制。
该目录也可以通过设置 “java.ext.dirs”来覆盖。
应用类加载器(Application or App Class-Loader)
负责加载应用程序路径下的类/classpath、系统变量java.class.path或者环境变量classpath指定的类。
类加载机制的三个特征
双亲委派模型
不是所有类加载都遵守这个模型
有的时候,启动类加载器所加载的类型,是可能要加载用户代码的,比如 JDK 内部的 ServiceProvider/ServiceLoader机制,用户可以在标准 API 框架上,提供自己的实现,JDK 也需要提供些默认的参考实现。
例如,Java 中 JNDI、JDBC、文件系统、Cipher 等很多方面,都是利用的这种机制,这种情况就不会用双亲委派模型去加载,而是利用所谓的上下文加载器。
具体可以参考我的这篇博客——哪些情况下类加载不需要遵守双亲委派模型?
可见性
子类加载器可以访问父加载器加载的类型,但是反过来是不允许的,不然,因为缺少必要的隔离,我们就没有办法利用类加载器去实现容器的逻辑。
单一性
由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会在子加载器中重复加载。
但是注意,类加载器“邻居”间,同一类型仍然可以被加载多次,因为互相并不可见。