首页 归档 关于 learn love 工具

Java类加载器及加载范围

加载器

对于虚拟机,只有两种不同的类加载器:

  1. 启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;
  2. 其它所有的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader。

具体划分,如下:

  1. 启动类加载器(Bootstrap ClassLoader):这个类加载器负责将放置在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定路径中的,并且是虚拟机能识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放置在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接使用,因为是C++语言实现的。
  2. 扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
  3. 应用程序类加载器(Application ClassLoader):这个类加载器由sum.misc.Launcher.$AppClassLoader来实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也被称为系统类加载器。它负责加载用户类路径上所指定的类库(classPath),开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

应用程序由这三种类加载器互相配合进行加载的,如果有必须,还可以加入自己定义的类加载器。这些类加载器之间的关系一般如下图

上图中展示的类加载器之间的层次关系,就称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码。

双亲委派模型

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完全这个加载请求时(查看该类是否是在自己的加载范围),子加载器才会尝试自己去加载。

双亲委派的好处:

  1. 安全性:使用双亲委派模型来组织类加载器之间的关系,有一个很明显的好处,就是Java类随着它的类加载器(说白了,就是它所在的目录)一起具备了一种带有优先级的层次关系,这对于保证Java程序的稳定运作很重要。例如,类java.lang.Object类存放在JDK\jre\lib下的rt.jar之中,因此无论是哪个类加载器要加载此类,最终都会委派给启动类加载器进行加载,这边保证了Object类在程序中的各种类加载器中都是同一个类,可以防止java核心类被篡改。

  2. 避免重复加载:当使用了双亲委派机制后,当一个类已经被上级类加载器加载后,下级类加载器就不会出现重复加载的情况。

使用场景

Class.getClassLoader().getResource(String path)中,path不能以'/'开头,path是指类加载器的加载范围,在资源加载的过程中,使用的逐级向上委托的形式加载的,'/'表示Boot ClassLoader,类加载器中的加载范围,因为这个类加载器是C++实现的,所以加载范围为null。如下所示:参考

public class Test
{
    public static void main(String[] args)
    {
        System.out.println(Test.class.getClassLoader().getResource(""));
        System.out.println(Test.class.getClassLoader().getResource("/"));
    }
}

Class.getResource和ClassLoader.getResource

Class.getResource(String path)

  • path不以’/'开头时,默认是从此类所在的包下取资源;
  • path 以’/'开头时,则是从ClassPath根下获取;

Class.getResource和Class.getResourceAsStream在使用时,路径选择上是一样的。

Class.getClassLoader().getResource(String path)

ClassLoader 代表的是用来加载其他类的类,任何一个class都包含对加载这个class的类加载器(ClassLoader)的引用,所以其加载资源一定是绝对路径,且不能以‘/’开头

  • path不能以‘/’开头;
  • path是从ClassPath根下获取

Class.getClassLoader().getResource和Class.getClassLoader().getResourceAsStream在使用时,路径选择上也是一样的。

参考

https://juejin.cn/post/6844904159993397262