Java三个类加载器及它们的相互关系

 更新时间:2021年06月07日 11:03:15   作者:cswhl  
Java在需要使用类别的时候,才会将类别加载,Java的类别载入是由类别载入器(Class loader)来达到的,预设上,在程序启动之后,主要会有三个类别加载器,文中详细介绍了这三个类加载器,需要的朋友可以参考下

一、什么是类加载器?

虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”

类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,它最初是为了满足Java Applet的需求而开发出来的。虽然目前Java Applet技术基本上已经“死掉”,但类加载器却在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为了Java技术体系中一块重要的基石,可谓是失之桑榆,收之东隅。

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

二、AppClassLoader系统类加载器

AppClassLoader应用类加载器,又称为系统类加载器,负责在JVM启动时,加载来自命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR类包和类路径

  • 获取当前类的类加载器:
public class AppClassLoaderTest {

    public static void main(String[] args) {
        System.out.println(ClassLoader.getSystemClassLoader());
    }

}

输出:

sun.misc.Launcher$AppClassLoader@73d16e93

这说明AppclassLoader是当前应用classpath所有类的加载器。

查看ClassLoader的源码可发现:在没有特定说明的情况下,用户自定义的任何类加载器都将该类加载器作为自定义类加载器的父加载器.

  • 通过执行下面的代码即可获得classpath的加载路径:
String classPath = System.getProperty("java.class.path");
for (String path : classPath.split(";")) {
    System.out.println(path);
}

输出:

.

通常是当前执行字节码的路径。

  • main函数的类的加载就是使用AppClassLoader加载器进行加载的,而AppClassLoader的父加载器是ExtClassLoader:
public class AppClassLoaderTest {

    public static void main(String[] args) {
        ClassLoader classLoader = Test.class.getClassLoader();
        System.out.println(classLoader);
        System.out.println(classLoader.getParent());
    }

    private static class Test {

    }

}

输出:

sun.misc.Launcher$AppClassLoader@73d16e93 
sun.misc.Launcher$ExtClassLoader@15db9742 

三、ExtClassLoader扩展类加载器

ExtClassLoader称为扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包或者由java.ext.dirs系统属性指定的jar包.放入这个目录下的jar包对AppClassLoader加载器都是可见的(因为ExtClassLoader是AppClassLoader的父加载器,并且Java类加载器采用了委托机制).

  • ExtClassLoader的类扫描路径通过执行下面代码来看一下:
public class ExtClassLoaderTest {
	
	public static void main(String[] args) {
		String extDirs = System.getProperty("java.ext.dirs");
		for (String path : extDirs.split(";")) {
			System.out.println(path);
		}
	}
}

执行结果如下:

D:\TOOLS\JAVA\lib\ext 
C:\WINDOWS\Sun\Java\lib\ext 

  • 从上面的路径中随意选择一个类,来看看它的类加载器是什么:
public class ExtClassLoaderTest {
	
	public static void main(String[] args) {
		ClassLoader classLoader = sun.security.ec.SunEC.class.getClassLoader();
		System.out.println(classLoader);
		System.out.println(classLoader.getParent());
	}
}

输出:

sun.misc.Launcher$ExtClassLoader@30f39991 
null 

从输出结果可知ExtClassLoader的父加载器为null

四、BootstrapClassLoader启动类加载器

启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等

  • 通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件:
import java.net.URL;

public class BootstraplassLoaderPath {
	
	public static void main(String[] args) {
		URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		for (URL url : urLs) {
    	System.out.println(url.toExternalForm());
		}
	}
}

输出:

file:/D:/TOOLS/JAVA/lib/resources.jar
file:/D:/TOOLS/JAVA/lib/rt.jar
file:/D:/TOOLS/JAVA/lib/sunrsasign.jar
file:/D:/TOOLS/JAVA/lib/jsse.jar
file:/D:/TOOLS/JAVA/lib/jce.jar
file:/D:/TOOLS/JAVA/lib/charsets.jar
file:/D:/TOOLS/JAVA/lib/jfr.jar
file:/D:/TOOLS/JAVA/classes

  • 从rt.jar中选择String类,看一下String类的类加载器是什么:
public class stringBootstraplassLoaderTest {
	
	public static void main(String[] args) {
		ClassLoader classLoader = String.class.getClassLoader();
    System.out.println(classLoader);
	}
}

输出:

null

由于BootstrapClassLoader对Java不可见,所以返回了null,我们也可以通过某一个类的加载器是否为null来作为判断该类是不是使用BootstrapClassLoader进行加载的依据。
另外上面提到ExtClassLoader的父加载器返回的是null,那是否说明ExtClassLoader的父加载器是BootstrapClassLoader.

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。

五、加载器关系总结

JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器,Bootstrap则是ExtClassLoader的父加载器.
关系图如下:

六、参考资料

1.Java类加载器
2.一看你就懂,超详细java中的ClassLoader详解

到此这篇关于Java三个类加载器及它们的相互关系的文章就介绍到这了,更多相关Java类加载器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java删除二叉搜索树的任意元素的方法详解

    Java删除二叉搜索树的任意元素的方法详解

    这篇文章主要介绍了Java删除二叉搜索树的任意元素的方法,结合实例形式详细分析了java这对二叉搜索树的遍历、查找、删除等相关操作技巧与使用注意事项,需要的朋友可以参考下
    2020-03-03
  • Springboot之restTemplate配置及使用方式

    Springboot之restTemplate配置及使用方式

    这篇文章主要介绍了Springboot之restTemplate配置及使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • 详解Elastic Search搜索引擎在SpringBoot中的实践

    详解Elastic Search搜索引擎在SpringBoot中的实践

    本篇文章主要介绍了Elastic Search搜索引擎在SpringBoot中的实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • java List中相同的数据合并到一起

    java List中相同的数据合并到一起

    这篇文章主要介绍了java List中相同的数据合并到一起,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • 一文详解Spring是怎样处理循环依赖的

    一文详解Spring是怎样处理循环依赖的

    循环依赖简单理解就是A,B 两个bean相互依赖,A依赖B,B依赖A,A->B、B->A大概就是这样,这篇文章主要介绍了Spring是怎样处理循环依赖的,文中通过代码示例给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2024-01-01
  • Java实现选择排序

    Java实现选择排序

    这篇文章主要介绍了Java实现选择排序,把一列数组按从小到大或从大到小排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Redis缓存及热点key问题解决方案

    Redis缓存及热点key问题解决方案

    这篇文章主要介绍了Redis缓存及热点key问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • java实现堆的操作方法(建堆,插入,删除)

    java实现堆的操作方法(建堆,插入,删除)

    下面小编就为大家分享一篇java实现堆的操作方法(建堆,插入,删除),具有很好的参考价值,希望对大家有所帮助
    2017-12-12
  • Spring的@Conditional详解

    Spring的@Conditional详解

    这篇文章主要介绍了Spring的@Conditional详解,给想要注入Bean增加限制条件,只有满足限制条件才会被构造并注入到Spring的IOC容器中,通常和@Bean注解一起使用,需要的朋友可以参考下
    2024-01-01
  • SWT(JFace)体验之打开多个Form

    SWT(JFace)体验之打开多个Form

    SWT(JFace)体验之打开多个Form的实现代码。
    2009-06-06

最新评论