我们都知道 Java 程序的运行是以 JVM 为基础的,JVM 即 Java 虚拟机。
而 JVM 会默认提供三个主要的类加载器:
- BootStrap:引导类加载器
- ExtClassLoader:扩展类加载器
- AppClassLoader:系统类加载器
分别详细介绍下:
BootStrap 是用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader;
ExtClassLoader 加载 JVM 提供的扩展库目录下的 Java 类;
AppClassLoader 是根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。
除了 BootStrap 外,每个类加载器都有一个「父'类加载器」,通过类加载器调用 getParent() 方法能够得到他的「父'类加载器」。
现实项目中,我们可能会编写自己的类去继承「java.lang.ClassLoader」来实现我们自己的类加载器,从而达到某种目的。一般我们创建的类加载器的「父'类加载器」是 AppClassLoader。
而 AppClassLoader 的「父'类加载器」是ExtClassLoader;
ExtClassLoader 的「父'类加载器」则是 BootStrap。
所以我们使用类加载器的结构用一张「树形图」可以很直白的表现出来。
为了校验,写了一个简单的例子来验证一下:
得到的结果是
我们自定义的类加载器的父类加载器是 AppClassLoader;
而 AppClassLoader 的父类加载器是 ExtClassLoader;
ExtClassLoader 的父类加载器打印是 null,原因是他并不是 java 类。
好了,类加载器的层级结构我们明白之后,我们再来看下类加载器的委托机制
双亲委托
我们先来看个例子吧,这个例子由 eclipse 编写。
例1:
打印的结果是
说明了加载我们当前类的类加载器是 AppClassLoader
而 System 是属于系统提供jar类:rt.jar 中的类,所以是由 BootStrap 加载的,所以返回结果为 null。
不过这个例子并不能观察出 “类加载器的委托机制”。我们需要将此项目打包成 jar 文件,然后再放到 ext/目录 下,然后再来运行看看:
例2:
这个时候发现打印结果是不是变成了 ExtClassLoader。
对比一下我们在 eclipse 运行的代码 打印结果是 AppClassLoader,是不是看出点什么端倪?
其实这只是类加载器的委托机制的表象,我们还是看一下它的加载机制是什么吧。
我们自定义的类会向 AppClassLoader 发送加载请求
AppClassLoader 会向 ExtClassLoader 发送加载请求
ExtClassLoader 则会向 BootStrap 发送加载请求
此时 BootStrap 由于没有父类加载器,所以他会尝试加载我们自定义的类,但是由于BootStrap 的职责是加载 Java 的核心库,而在核心库中并未找到自定义类,所以无法加载。
此时会将加载权给 ExtClassLoader,ExtClassLoader 的职责是加载 lib/ext 库中的库,而我们第一个例子中没有将 jar 包放到 ext 目录下,所以第一个例子中 ExtClassLoader 没有加载到。
而第二个例子中,我们把 jar 包放到了 ext 下,所以 ExtClassLoader 加载到了。
继续第一个例子,ExtClassLoader 没有加载到自定义类,所以把加载权给了 AppClassLoader,而 AppClassLoader 就是为加载这种自定义类而存在的,自然也就加载了。
整理成流程图则是这样的
下篇网站我会创建一个自定义的类加载器,然后分析一下 ClassLoader 类的加载流程。