既然有这么多种类的来源,那么在 Java 里,是由某一个具体的 ClassLoader 来统一加载呢?还是由多个 ClassLoader 来协作加载呢?
哪些 ClassLoader 负责加载上面几类 Class?
实际上,针对上面四种来源的类,分别有不同的加载器负责加载。
首先,我们来看级别最高的 Java 核心类 ,即$JAVA_HOME/jre/lib 里的核心 jar 文件。这些类是 Java 运行的基础类,由一个名为 BootstrapClassLoader 加载器负责加载,它也被称作 根加载器/引导加载器。注意,BootstrapClassLoader 比较特殊,它不继承 ClassLoader,而是由 JVM 内部实现;
然后,需要加载 Java 核心扩展类 ,即 $JAVA_HOME/jre/lib/ext 目录下的 jar 文件。这些文件由 ExtensionClassLoader 负责加载,它也被称作 扩展类加载器。当然,用户如果把自己开发的 jar 文件放在这个目录,也会被 ExtClassLoader 加载;
接下来是开发者在项目中编写的类,这些文件将由 AppClassLoader 加载器进行加载,它也被称作 系统类加载器 System ClassLoader;
最后,如果想远程加载如(本地文件/网络下载)的方式,则必须要自己自定义一个 ClassLoader,复写其中的 findClass() 方法才能得以实现。
因此能看出,Java 里提供了至少四类 ClassLoader 来分别加载不同来源的 Class.
那么,这几种 ClassLoader 是如何协作来加载一个类呢?
这些 ClassLoader 以何种方式来协作加载 String 类呢?
String 类是 Java 自带的最常用的一个类,现在的问题是,JVM 将以何种方式把 String class 加载进来呢?
我们来猜想下。
首先,String 类属于 Java 核心类,位于 $JAVA_HOME/jre/lib 目录下。有的朋友会马上反应过来,上文中提过了,该目录下的类会由 BootstrapClassLoader 进行加载。没错,它确实是由 BootstrapClassLoader 进行加载。但,这种回答的前提是你已经知道了 String 在 $JAVA_HOME/jre/lib 目录下。
那么,如果你并不知道 String 类究竟位于哪呢?或者我希望你去加载一个 unknown 的类呢?
有的朋友这时会说,那很简单,只要去遍历一遍所有的类,看看这个 unknown 的类位于哪里,然后再用对应的加载器去加载。
是的,思路很正确。那应该如何去遍历呢?
比如,可以先遍历用户自己写的类,如果找到了就用 AppClassLoader 去加载;否则去遍历 Java 核心类目录,找到了就用 BootstrapClassLoader 去加载,否则就去遍历 Java 扩展类库,依次类推。
这种思路方向是正确的,不过存在一个漏洞。
假如开发者自己伪造了一个 java.lang.String 类,即在项目中创建一个包java.lang,包内创建一个名为 String 的类,这完全可以做到。那如果利用上面的遍历方法,是不是这个项目中用到的 String 不是都变成了这个伪造的 java.lang.String 类吗?如何解决这个问题呢?
解决方法很简单,当查找一个类时,优先遍历最高级别的 Java 核心类,然后再去遍历 Java 核心扩展类,最后再遍历用户自定义类,而且这个遍历过程是一旦找到就立即停止遍历。
在 Java 中,这种实现方式也称作 双亲委托。其实很简单,把 BootstrapClassLoader 想象为核心高层领导人, ExtClassLoader 想象为中层干部, AppClassLoader 想象为普通公务员。每次需要加载一个类,先获取一个系统加载器 AppClassLoader 的实例(ClassLoader.getSystemClassLoader()),然后向上级层层请求,由最上级优先去加载,如果上级觉得这些类不属于核心类,就可以下放到各子级负责人去自行加载。
以上就是潍坊计算机培训给大家做的内容详解,更多关于IT课程的学习,请继续关注潍坊计算机培训。