java类如何加载

article/2025/10/12 6:51:34

最近研究java的进阶知识,先从java类加载机制学起,原先看过《深入理解java虚拟机》这本书,奈何书的知识面太广及自身只看了一遍,很多知识并不是很理解,今天看了几篇别人讲解的java类加载机制,觉得讲的很好,帮助很大,下面把别人的摘录过来,有时间多看看。

什么是 Java 类加载机制?

Java 虚拟机一般使用 Java 类的流程为:首先将开发者编写的 Java 源代码(.java文件)编译成 Java 字节码(.class文件),然后类加载器会读取这个 .class 文件,并转换成 java.lang.Class 的实例。有了该 Class 实例后,Java 虚拟机可以利用 newInstance 之类的方法创建其真正对象了。

ClassLoader 是 Java 提供的类加载器,绝大多数的类加载器都继承自 ClassLoader,它们被用来加载不同来源的 Class 文件。

类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。

其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。
在这里插入图片描述

1、加载
简单的说,类加载阶段就是由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例(Java虚拟机规范并没有明确要求一定要存储在堆区中,只是hotspot选择将Class对戏那个存储在方法区中),这个Class对象在日后就会作为方法区中该类的各种数据的访问入口。

2、链接
链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中,经由验证、准备和解析三个阶段。
1)、验证
验证类数据信息是否符合JVM规范,是否是一个有效的字节码文件,验证内容涵盖了类数据信息的格式验证、语义分析、操作验证等。
格式验证:验证是否符合class文件规范
语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法视频被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否通过富豪引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
2)、准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)
被final修饰的静态变量,会直接赋予原值;类字段的字段属性表中存在ConstantValue属性,则在准备阶段,其值就是ConstantValue的值
3)、解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。
可以认为是一些静态绑定的会被解析,动态绑定则只会在运行是进行解析;静态绑定包括一些final方法(不可以重写),static方法(只会属于当前类),构造器(不会被重写)

3、初始化
将一个类中所有被static关键字标识的代码统一执行一遍,如果执行的是静态变量,那么就会使用用户指定的值覆盖之前在准备阶段设置的初始值;如果执行的是static代码块,那么在初始化阶段,JVM就会执行static代码块中定义的所有操作。

所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊的方法中,这个方法就是方法,即类/接口初始化方法。该方法的作用就是初始化一个中的变量,使用用户指定的值覆盖之前在准备阶段里设定的初始值。任何invoke之类的字节码都无法调用方法,因为该方法只能在类加载的过程中由JVM调用。

如果父类还没有被初始化,那么优先对父类初始化,但在方法内部不会显示调用父类的方法,由JVM负责保证一个类的方法执行之前,它的父类方法已经被执行。
JVM必须确保一个类在初始化的过程中,如果是多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。

Class 文件有哪些来源呢?

上文提到了 ClassLoader 可以去加载多种来源的 Class,那么具体有哪些来源呢?

首先,最常见的是开发者在应用程序中编写的类,这些类位于项目目录下;

然后,有 Java 内部自带的核心类如 java.lang、java.math、java.io 等 package 内部的类,位于 $JAVA_HOME/jre/lib/ 目录下,如 java.lang.String 类就是定义在 $JAVA_HOME/jre/lib/rt.jar 文件里;

另外,还有 Java 核心扩展类,位于 $JAVA_HOME/jre/lib/ext 目录下。开发者也可以把自己编写的类打包成 jar 文件放入该目录下;

最后还有一种,是动态加载远程的 .class 文件。

既然有这么多种类的来源,那么在 Java 里,是由某一个具体的 ClassLoader 来统一加载呢?还是由多个 ClassLoader 来协作加载呢?

哪些 ClassLoader 负责加载上面几类 Class?

实际上,针对上面四种来源的类,分别有不同的加载器负责加载。

首先,我们来看级别最高的 Java 核心类,即$JAVA_HOME/jre/lib 里的核心 jar 文件。这些类是 Java 运行的基础类,由一个名为 BootstrapClassLoader 加载器负责加载,它也被称作 根加载器/引导加载器。注意,BootstrapClassLoader 比较特殊,它不继承 ClassLoader,而是由 JVM 内部实现;

然后,需要加载 Java 核心扩展类,即 $JAVA_HOME/jre/lib/ext 目录下的 jar 文件。这些文件由 ExtensionClassLoader 负责加载,它也被称作 扩展类加载器。当然,用户如果把自己开发的 jar 文件放在这个目录,也会被 ExtClassLoader 加载;

接下来是开发者在项目中编写的类,这些文件将由 AppClassLoader 加载器进行加载,它也被称作 系统类加载器 System ClassLoader;

最后,如果想远程加载如(本地文件/网络下载)的方式,则必须要自己自定义一个 ClassLoader,复写其中的 findClass() 方法才能得以实现。

因此能看出,Java 里提供了至少四类 ClassLoader 来分别加载不同来源的 Class。
在这里插入图片描述

不同加载器是如何工作的?什么是双亲委托模型及双亲委托存在的意义。

String 类是 Java 自带的最常用的一个类,现在的问题是,JVM 将以何种方式把 String class 加载进来呢?

我们来猜想下。

首先,String 类属于 Java 核心类,位于 $JAVA_HOME/jre/lib 目录下。有的朋友会马上反应过来,上文中提过了,该目录下的类会由 BootstrapClassLoader 进行加载。没错,它确实是由 BootstrapClassLoader 进行加载。但,这种回答的前提是你已经知道了 String 在 $JAVA_HOME/jre/lib 目录下。

那么,如果你并不知道 String 类究竟位于哪呢?或者我希望你去加载一个 unknown 的类呢?

有的朋友这时会说,那很简单,只要去遍历一遍所有的类,看看这个 unknown 的类位于哪里,然后再用对应的加载器去加载。

是的,思路很正确。那应该如何去遍历呢?

比如,可以先遍历用户自己写的类,如果找到了就用 AppClassLoader 去加载;否则去遍历 Java 核心类目录,找到了就用 BootstrapClassLoader 去加载,否则就去遍历 Java 扩展类库,依次类推。

这种思路方向是正确的,不过存在一个漏洞。

假如开发者自己伪造了一个 java.lang.String 类,即在项目中创建一个包java.lang,包内创建一个名为 String 的类,这完全可以做到。那如果利用上面的遍历方法,是不是这个项目中用到的 String 不是都变成了这个伪造的 java.lang.String 类吗?如何解决这个问题呢?

当一个类加载器接收到一个类加载的任务时,不会立即展开加载,而是将加载任务委托给它的父类加载器去执行,每一层的类都采用相同的方式,直至委托给最顶层的启动类加载器为止。如果父类加载器无法加载委托给它的类,便将类的加载任务退回给下一级类加载器去执行加载。

双亲委托模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。

使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委托给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种加载器环境中都是同一个类。相反,如果没有使用双亲委托模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果自己去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。

双亲委托模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委托的代码都集中在java.lang.ClassLoader的loadClass()方法中,逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载器加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass方法进行加载。

类加载器的应用:自定义类加载器

自定义类加载器,它允许我们在运行时可以从本地磁盘或网络上动态加载自定义类。这使得开发者可以动态修复某些有问题的类,热更新代码。

自定义类加载器需要继承抽象类ClassLoader,实现findClass方法,该方法会在loadClass调用的时候被调用,findClass默认会抛出异常。不是loadClass()方法,因为ClassLoader提供了loadClass()(如上面的源码),它会基于双亲委托机制去搜索某个 class,直到搜索不到才会调用自身的findClass(),如果直接复写loadClass(),那还要实现双亲委托机制

findClass方法表示根据类名查找类对象
loadClass方法表示根据类名进行双亲委托模型进行类加载并返回类对象
defineClass方法表示跟根据类的字节码转换为类对象

https://www.cnblogs.com/xh_chiang/p/7575869.html
https://www.cnblogs.com/xiaoxian1369/p/5498817.html


http://chatgpt.dhexx.cn/article/ZE7fXVj6.shtml

相关文章

JVM——Java类加载机制总结

1. 类加载器的组织结构 转载请注明出处:JVM——Java类加载机制总结_SEU_Calvin的博客-CSDN博客 类加载器 ClassLoader是具有层次结构的,也就是父子关系。其中,Bootstrap是所有类加载器的父亲。 (1)Bootstrapclass lo…

深入理解Java类加载器(1):Java类加载原理解析

1 基本信息 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载。Java的类加载机制是技术体系中比较核心的部分,虽然和大部分开发人员直接打交道不多,但是对其背后的机理有一定理解…

深入理解Java类加载器(一):Java类加载原理解析

摘要: 每个开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这个异常背后涉及到的是Java技术体系中的类加载机制。本文简述了JVM三种预定义类加载器,即启动类加载器、扩展类加载器和系统类加载器,并介绍和分析它…

【Java · 类加载】类加载器

1. 概述 类加载器是 JVM 执行类加载机制的前提。 ClassLoader的作用: ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目…

java类加载

当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。 一、类加载过程 加载指…

Java的类加载

1、类的加载过程 Java文件通过javac编译成.class二进制字节码文件,然后交给类加载器加载到我们的虚拟机内存中,最后我们通过new 对象来实现对象的初始化,才能调用和执行这个类,类的加载有三步:加载、连接、初始化。 1.1、加载 …

Java类的加载

类的生命周期 当java源代码文件被javac编译成class文件后,并不能直接运行,而是需要经过加载,连接和初始化这几个阶段后才能使用。在使用完类或被销毁后,JVM会将类卸载掉。 类加载的过程 类加载的过程需要经过三个阶段分别是&a…

「WAVE SUMMIT 2022深度学习开发者峰会」火热来袭

「WAVE SUMMIT 2022深度学习开发者峰会」火热来袭! 会上您将了解以飞桨为代表的深度学习领域的最新技术突破! 看最新的AI技术如何助力我国产业发展,先人一步了解技术风向。 5月20日 13:00期待与您相聚云端 报名即送好礼,快来参与吧…

Wave(.wav)文件格式

Wave文件格式主要是用来存储音频PCM数据的,其实也可以存储非PCM音频数据,这种情况我们就不考虑了。文件的扩展名为“.wav”,采用RIFF文件结构。 一、RIFF文件格式简介 1、RIFF文件是由一个一个的chunk组成的,并且chunk之间可以嵌…

wave文件格式

Wave文件采用RIFF文件格式,总体来看Wave文件是由多个chunk嵌套组成的;Wave文件最外层是一个标识为"RIFF"的类型块chunk1;在chunk1的data部分嵌套了2个chunk,即chunk2和chunk3;chunk2的标识为"fmt"…

WAVE 文件格式分析

WAVE 文件作为多媒体中使用的声音波形文件格式之一,它是以RIFF(Resource Interchange File Format)格式为标准的。每个WAVE文件的头四个字节便是“RIFF”。WAVE 文件由文件头和数据体两大部分组成。其中文件头又分为 RIFF/WAV 文件…

html wave标签,html wave的实现

如何实现wave动画?求demo 我理解的关键点: 1、色彩的均匀渐变,比较柔和,没有明显的过渡层 2、不同频率的正弦波 我的实现: 效果相差很大! let canvas1 = document.querySelector(#canvas1); let ctx = canvas1.getContext(2d); canvas1.width = canvas1.parentNode.offset…

802.11ac Wave2 将为WIFI性能和效果带来提升

Wi-Fi联盟推出的第二代802.11ac认证项目,使Wi-Fi网络的速度和效率又向前迈了一大步。 802.11ac正式颁布于2014年,标准包含了很多新特性,这些特性受到了Wi-Fi供应商和消费者的欢迎,特性包括: 采用5GHz频段 更宽的通道 更…

802.11ac wave2的前世今生

2015年下半年,高通、博通、RTL等芯片厂商相继发布了满足802.11ac wave2要求的芯片,WLAN及终端厂商也迅速跟进推出相应的产品和终端。802.11ac wave2在多方推动下于2015年底实现了商用,标志着802.11ac进入了新的发展阶段。 突破多用户并发能力…

什么是Wi-Fi Wave 2?

802.11ac的第二波浪潮(即Wave 2)已经拍到了岸边,而新的MU-MIMO(多用户、多路输入多路输出)技术也正在引起业界的骚动。它正是我们已经看到的Wi-Fi的巨大潜力之一,可大大提升无线网络的吞吐量,并将在密度和容量上与之前的无线网络产生巨大差异…

什么是802.11ac和802.11ac Wave2

什么是802.11ac和802.11ac Wave2 简介什么是802.11ac802.11ac VS 802.11n802.11ac Wave1的关键技术802.11ac Wave2 VS 802.11ac Wave1802.11ac Wave2的关键特性 简介 从1997年第一代802.11标准802.11发布至今,Wi-Fi经历了巨大的发展和普及,802.11ac的推…

wave2lip训练

论文:https://arxiv.org/pdf/2008.10010.pdf 训练源码:https://github.com/Rudrabha/Wav2Lip 经过调整过的代码:https://gitee.com/sparkle__code__guy/wave2lip ffmpeg的安装:https://blog.csdn.net/sslfk/article/details/1…

802.11ac wave 2:你需要知道的6件事

Wi-Fi联盟是全球非盈利性组织,旨在改善Wi-Fi技术的互操作性。近日,Wi-Fi联盟认证了802.11ac wave 2标准,新标准带来了一系列的更新,最终可能会影响消费者及企业的日常连接。 下面让我们看看这六个性能变化: 1.可能实现…

被吹上天的802.11ac Wave2到底好在哪?还有,Wave1哪去了?

802.11ac Wave2到底好在哪? 在说Wave2之前,我们先要了解802.11ac是什么,它的特点是什么。IEEE 802.11ac是802.11的一个通信标准,802.11n的升级版。再具体的渊源这里就不讲了,感兴趣的可自行查阅资料。重点来了&#x…