Java的类加载器

回顾JVM类加载机制,共分为加载、链接和初始化三个阶段,其中加载阶段做的第一件事,即通过一个类的全限定名来获取此类的二进制字节流,就是类加载器要做的事情。所以类加载器就是JVM通过一个类的全限定名来获取该类的二进制字节流的代码块。

JVM类加载器的双亲委派模型,如下图所示。类加载器之间的父子关系并不是父类和子类的继承关系,而是通过使用组合来实现复用父加载器的代码。

1.启动类加载器(Bootstrap ClassLoader)

也叫根类加载器,负责加载Java的核心类库,即JAVA_HOME/jre/lib目录下的jar包,比如rt.jar(包含System、String这样的核心类)等。启动类加载器是C++实现的,并不是Java实现,因此它不是java.lang.ClassLoader的子类。

2.扩展类加载器(Extension ClassLoader)

负责加载扩展目录%JAVA_HOME%/jre/lib/ext下的jar包,用户可以把自己开发的类打成包放进去,以扩展Java核心类以外的新功能。扩展类加载器派生继承自java.lang.ClassLoader,其父加载器(注意不是父类!)是启动类加载器。

3.系统类加载器(System ClassLoader)

也叫应用程序类加载器,加载CLASSPATH环境变量指定的目录下的类,一般来说就是用户在应用程序中自定义的类、以及依赖的第三方jar包等。系统类加载器派生继承自java.lang.ClassLoader,其父加载器(注意不是父类!)是扩展类加载器。

4.自定义类加载器

一般情况下,以上3种加载器能满足我们日常的开发工作,不满足时我们还可以自定义类加载器。比如用网络加载Java类,为了保证传输中的安全性,采用了加密操作,那么以上3种加载器就无法加载这个类,这时候就需要自定义类加载器。实现方法是继承java.lang.ClassLoader类重写findClass()方法

类加载的双亲委派机制:

  • 如果一个类加载器接收到了类加载的请求,它自己不会先去加载,会把这个请求委托给父类加载器去执行。
  • 如果父类还存在父类加载器,则继续向上委托,一直委托到启动类加载器。
  • 如果父类加载器可以完成加载任务,就返回成功结果,如果父类加载失败,就由子类自己去尝试加载。

源码实现:

public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false); // 递归思想
} else {
// parent是null,说明已经递归到扩展类加载器了(因为扩展类加载器的parent是启动类加载器,而启动类加载器不是Java实现的,因此获取结果是null)
c = findBootstrapClassOrNull(name); // 因为无法通过parent取到启动类加载器,直接需要直接调用
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found from the non-null parent class loader父加载器找不到该类就抛异常
}
if (c == null) {
c = findClass(name); // 父加载器找不到,那就自己找
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
文章作者: Moon Lou
文章链接: https://loumoon.github.io/2021/03/10/Java的类加载器/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Moon's Blog