Java中类加载器举例详解

 更新时间:2026年01月07日 11:18:10   作者:小白qzx  
Java类加载器是Java语言的核心组件之一,负责从文件系统或网络中加载Class文件,Class文件在文件开头有特定的文件标识,这篇文章主要介绍了Java中类加载器的相关资料,需要的朋友可以参考下

类加载器简介

Java程序被编译器编译之后成为字节码文件(.class文件),当程序需要某个类时,虚拟机便会将对应的class文件进行加载,创建出对应的Class对象。而这个将class文件加载到虚拟机内存的过程,便是类加载。

类加载器负责在运行时将Java类动态加载到JVM(Java虚拟机),是JRE(Java运行时环境)的一部分。由于类加载器的存在,JVM无需了解底层文件或文件系统即可运行Java程序。并且Java类不会一次全部加载到内存中,而是在需要时才会加载到内存中。

类加载的过程

类的生命周期通常包括:加载、链接、初始化、使用和卸载。上图中包含了类加载的三个阶段:加载阶段、链接阶段和初始化阶段。如果将这三个阶段再拆分细化包括:加载、验证、准备、解析和初始化。

这几个阶段的作用简单概况一下:

  • 加载:通过一个类的完全限定查找类字节码文件,转化为方法区运行时的数据结构,创建一个代表该类的Class对象。
  • 验证:确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。
  • 准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值。不包含被final修饰的static变量,因为它在编译时已经分配了。
  • 解析:将常量池内的符号引用转换为直接引用的过程,比如(main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。如果符号引用指向一个未被加载的类,或者未被加载类的字段或方法,那么解析将触发这个类的加载。
  • 初始化:类加载最后阶段,执行静态初始化器和静态初始化成员变量。对类的静态变量初始化为指定的值,执行静态代码块。

在上述类加载的过程中,虚拟机提供了三种类加载器:启动(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器),此外还可以自定义类加载器。

启动(Bootstrap)类加载器:

系统中最顶层、最核心的加载器,是 JVM 与生俱来的一部分,负责加载 JDK 核心类库,奠定 Java 程序运行的基础,由 JVM 的底层代码(C/C++,如 HotSpot 虚拟机)实现,无法在 Java 代码中直接获取其对象(调用 getClassLoader() 返回 null)。

加载范围:加载 JDK 核心类库

  • JDK 8 及之前:加载 <JAVA_HOME>/jre/lib 目录下的核心 JAR 包(如 rt.jarcharsets.jarsunrsasign.jar 等),这些 JAR 是 JVM 识别的「核心类库」(非该目录下的任意 JAR 不会被加载);
  • JDK 9+(模块化):移除了 rt.jar 等打包方式,核心类以「模块」形式存储在 <JAVA_HOME>/lib/modules(模块化镜像文件),启动类加载器负责加载 java.base 等核心模块(如 java.langjava.io 所属的模块)。

扩展(Extension)类加载器:

扩展类加载器是启动类加载器的子类,Java语言编写,由sun.misc.Launcher$ExtClassLoader实现,父类加载器为启动类加载器,负责加载标准核心Java类的扩展。

扩展类加载器从JDK扩展目录(通常是$JAVA_HOME/lib/ext目录)或java.ext.dirs系统属性中指定的任何其他目录进行自动加载。

系统/应用(System)类加载器:

系统类加载器负责将所有应用程序级类加载到JVM中。它加载在类路径环境变量,-classpath或-cp命令行选项中找到的文件。它是扩展类加载器的子类。

自定义类加载器:

在大多数情况下,内置的三种类加载器就足够了。但是,在需要从本地硬盘驱动器或网络中加载类的情况下,需要打破双亲委派机制,则可能需要使用自定义类加载器。

下面介绍类加载器的加载机制--双亲委派机制。

双亲委派机制:

从图中可以清晰的看到双亲委派机制的具体过程:加载某个类时会先层层委托给父加载器寻找目标类加载,如果父加载器在自己的加载类路径下都找不到目标类,尝试向下加载。比如我们自己随便写的User类,最先会找应用程序类加载器加载,应用程序类加载器会先委托扩展类加载器加载,扩展类加载器再委托启动类加载器,顶层启动类加载器在自己的类加载路径里找了半天没找到User类,则向下退回加载User类的请求,扩展类加载器收到回复就自己加载,在自己的类加载路径里找了半天也没找到User类,又向下退回User类的加载请求给应用程序类加载器,应用程序类加载器于是在自己的类加载路径里找User类,结果找到了就自己加载了。双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载。

我们来看一下loadclass的源码:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

此方法负责加载给定名称参数的类。name参数为类的全限定名。先查找是否已加载该类,若无,则判断有无父类加载器parent,尝试给父类加载器去loadclass,其实就是一个递归的顺序。

为什么要双亲委派机制:

沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止
核心API库被随意篡改( 可以自己写一个String 类 会发现报错 )

避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加
载一次,保证被加载类的唯一性

打破双亲委派机制示例:

1、tomcat:tomcat就是属于一个web 程序,可能部署俩个应用程序,不同的应用程序可能会依赖一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离,就需要打破双亲委派机制。

2、热部署:热部署要求 “修改类 / JSP 后,无需重启应用即可生效”,而双亲委派下,类加载器加载类后,该类会被 JVM 缓存,类加载器不销毁,类就无法重新加载。因此需要自定义类加载器 + 销毁重建。

总结

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

相关文章

  • SpringMVC 响应数据和结果视图从环境搭建到实战全解析

    SpringMVC 响应数据和结果视图从环境搭建到实战全解析

    SpringMVC中,Controller方法的返回值决定了响应方式,本文详细讲解了SpringMVC的开发环境搭建、Controller方法返回值分类、转发与重定向机制,以及JSON异步交互的实现,本文介绍SpringMVC 响应数据和结果视图从环境搭建到实战全解析,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • Java使用多线程批次查询大量数据(Callable返回数据)方式

    Java使用多线程批次查询大量数据(Callable返回数据)方式

    今天给大家分享Java使用多线程批次查询大量数据(Callable返回数据)方式,多线程有好几种方式,今天说的方式比较好,实现Callable<> 这种方式能返回查询的数据,加上Future异步获取方式,查询效率大大加快,感兴趣的朋友一起看看吧
    2023-11-11
  • java开发使用StringUtils.split避坑详解

    java开发使用StringUtils.split避坑详解

    这篇文章主要为大家介绍了java开发使用StringUtils.split避坑详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • SpringMVC拦截器详解

    SpringMVC拦截器详解

    本篇文章主要介绍了SpringMVC拦截器配置及使用方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-07-07
  • java实现AES可逆加密算法

    java实现AES可逆加密算法

    这篇文章主要为大家详细介绍了java实现AES可逆加密算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • Java获取汉字拼音的全拼和首拼实现代码分享

    Java获取汉字拼音的全拼和首拼实现代码分享

    这篇文章主要介绍了Java获取汉字拼音的全拼和首拼实现代码分享,本文直接给出实现代码,需要的朋友可以参考下
    2015-06-06
  • SpringBoot整合RabbitMQ实现交换机与队列的绑定

    SpringBoot整合RabbitMQ实现交换机与队列的绑定

    这篇文章将通过几个实例为大家介绍一些SpringBoot中RabbitMQ如何绑定交换机(交换器)与队列,文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-05-05
  • Java访问权限原理与用法详解

    Java访问权限原理与用法详解

    这篇文章主要介绍了Java访问权限,结合实例形式详细分析了java构造者思想、包、访问修饰符等相关原理、应用与操作注意事项,需要的朋友可以参考下
    2020-02-02
  • Java实现时间戳转代码运行时长

    Java实现时间戳转代码运行时长

    这篇文章主要为大家详细介绍了如何使用Java实现时间戳转代码运行时长功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-06-06
  • Java多线程环境下死锁模拟

    Java多线程环境下死锁模拟

    这篇文章主要介绍了模拟Java多线程环境下的死锁,文章介绍一些死锁的产生条件的相关资料,具有一定的参考价值,需要的小伙伴可以参考一下,希望对你有所帮助
    2021-12-12

最新评论