Java类加载那些事

Java源文件(.java文件)被编译器编译后变为字节码形式的类文件(.class文件),Java类加载的过程就是JVM加载.class的二进制文件并且放到内存中,将数据放到方法区,并且在堆区构造一个java.lang.class对象,并且完成类的初始化的过程。

//下述片段引用自 Java的类加载机制是什么?

Java的类加载机制主要分为三个过程:加载、连接和初始化。

1.加载机制

Java的类加载机制主要分为三个过程:加载、连接和初始化。这三个过程的顺序是固定的,但是每个过程中的细节却是不同的。下面我们来详细介绍一下这三个过程。

1.1 加载

Java的类加载器会根据类的全限定名来加载类,当需要使用某个类时,如果该类还未被加载进内存,则需要执行一下步骤进行加载:

1.1.1. 通过类的全限定名找到对应的class文件,这里的class文件可以是.java文件经过编译之后生成的.class文件,也可以是通过其他方式生成的.class文件。

1.1.2 将class文件中的二进制数据读取到内存中,并将其转换为方法区的运行时数据结构。

1.1.3 创建由该类所属的java.lang.Class对象。该对象可以理解为,是对类的各种数据(如名称、访问修饰符、方法、成员变量等)的封装。

在加载类时,类加载器除了加载某个具体的类外,还需要将这个类所依赖的类也加入到内存中。这种依赖性是多层级的,也就是说,被依赖的类又可能会去依赖其他类,所以在加载一个类时,通常需要将其类图中所有的类都加载进来。

1.2 连接

Java虚拟机在加载类之后,需要对类进行连接,连接分为三个步骤:验证、准备和解析。

1.2.1. 验证:在这个步骤中,Java虚拟机主要确保所加载的类的正确性。验证过程主要包括文件格式验证、元数据验证、字节码验证和符号引用验证等。其目的在于确保目标.class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机运行时环境安全。

1.2.2. 准备:在准备阶段,Java虚拟机为类的静态变量分配内存,并设置变量的初始值。这里需要注意的是,在这个阶段中分配的内存并不包含那些用户自定义的初始化值,这些值在初始化阶段中进行设置。

1.2.3. 解析:Java在这个阶段中将常量池中的符号引用转为直接引用。通过符号引用,虚拟机得知该类访问其他的类或者类中的字段、方法等,但在类初始化时,需要缓存这些直接引用,以便于直接调用。

1.3 初始化

在类的准备阶段,Java虚拟机已经为静态变量分配了内存并设置了初值,但是这些静态变量”赋初值“的动作并没有完成。初始化阶段,会为静态变量设置用户自定义的初始化值,并执行类构造器<clinit>()方法,以执行初始化操作。

此时,类的准备和初始化阶段已经执行结束,Java的类加载机制总的过程也就结束了

//引用结束

注意,静态代码的初始化分为两步,连接的准备阶段包含初始化,最后又有一个初始化步骤,两者并不重叠,前者是给静态成员变量分配内存并且设置类型的初始值,后者是给静态成员变量设置用户指定的初始值。这段话有点拗口,代码来说明更清晰。

 1、父类 parent.java文件

public class Parent {static {System.out.println("Parent static block 1.");parentStaticIntVar = 3;}static Integer parentStaticIntVar = 2;static {System.out.println("Parent static block 2.");System.out.println("parentStaticIntVar=" + parentStaticIntVar);parentStaticIntVar = 4;}{System.out.println("Parent not static block 1.");parentIntVar = 30;}Integer parentIntVar = 20;public Parent(){	System.out.println("Parent construct  method .");System.out.println("parentIntVar=" + parentIntVar);}public void f(){System.out.println("parent f().");}{System.out.println("Parent not static block 2.");parentIntVar = 40;}}

2、子类 Sub.java文件

public class Sub extends Parent {static {System.out.println("Sub static block 1.");subStaticIntVar = 3;}static Integer subStaticIntVar = 2;static {System.out.println("Sub static block 2.");System.out.println("subStaticIntVar=" + subStaticIntVar);subStaticIntVar = 4;}{System.out.println("Sub not static block 1.");subIntVar = 30;}Integer subIntVar = 20;public Sub(){	System.out.println("Sub construct  method .");System.out.println("subIntVar=" + subIntVar);}public void f(){System.out.println("Sub f().");}{System.out.println("Sub not static block 2.");subIntVar = 40;}}

3、TestMain.java文件

public class TestMain {public static void main(String[] args) {System.out.println("-----class-----");System.out.println(">>>subStaticIntVar=" + Sub.subStaticIntVar);System.out.println(">>>parentStaticIntVar=" + Sub.parentStaticIntVar);System.out.println("-----instance-----");Sub s = new Sub();s.f();}}

4、输出结果

-----class-----
Parent static block 1.
Parent static block 2.
parentStaticIntVar=2
Sub static block 1.
Sub static block 2.
subStaticIntVar=2
>>>subStaticIntVar=4
>>>parentStaticIntVar=4
-----instance-----
Parent not static block 1.
Parent not static block 2.
Parent construct  method .
parentIntVar=40
Sub not static block 1.
Sub not static block 2.
Sub construct  method .
subIntVar=40
Sub f().

解读一下后可以知道静态代码块和静态变量遵循如下规则:

1、静态代码块先于构造方法执行;

2、静态代码块可以给静态成员变量赋值;

3、静态代码块之间按照先后顺序执行;

4、父类的静态代码块先于子类的静态代码块执行;

5、静态代码块先于非静态代码块执行;

6、静态代码块在第一次使用这个类的时候执行,并且只执行一次;

7、静态变量的显式赋值和静态代码块的按照先后顺序执行;

实际上每个Java源文件由编辑器编译后,会自动给类加载器追加一个类初始化方法:<clinit>(),一个类只有一个,包含静态变量的显式赋值代码和静态代码块的代码,在源文件中看起来是一个一个独立的代码块,实际上编译后都放到一个这个类初始化方法中去了。

非静态的代码块和变量的规则和上述类似。

类加载的方法>>>

1、Class.forName("")

Class<?> c = Class.forName("com.example.zhangzk.reflect.TestServiceImpl");

加载类,并且完成初始化。

2、ClassLoader.loadClass("")

Class<?> c = Thread.currentThread().getContextClassLoader().loadClass("com.example.zhangzk.reflect.TestServiceImpl");

加载类,不初始化。

加载器有哪些>>>

启动类加载器,C++编写的,进入Java世界的大门,负责加载存放在$JAVA_HOME\jre\lib下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。

扩展类加载器,Java编写的,父加载器为启动类加载器,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载$JAVA_HOME\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类)。

应用程序类加载器,Java编写的,父加载器为扩展类加载器,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

自定义类加载器,Java编写的,父加载器为应用程序类加载器,典型代表是Tomcat,为了在一个TOMCAT进程下部署多个JAVA应用程序必须要自定义类加载器,进行应用隔离。

双亲委派模型>>>

每个类加载器需要加载类的时候,先请求父类加载器来加载,一直到启动类加载器,启动类加载器是没有父类加载器的,父类加载器找不到给类才由自己来加载。

要想搞清楚Spring Boot的启动流程,必须要要知道上述区别,只有充分利用好上述差异才能精准的控制加载和初始化的过程,Spring中这些都用的出神入化了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/164587.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动态规划从入门到精通

目录 动态规划的详解 动态规划的应用 机器人到达指定位置数 换钱的最少货币数 排成一条线的纸牌博弈问题 象棋中马的跳法 Bob的生存概率 换钱的方法数 动态规划的总结 动态规划的详解 暴力尝试递归操作中有很多重复计算的操作&#xff0c;浪费时间。动态规划就是减少暴力…

大模型增量预训练参数说明

在增量预训练过程中通常需要设置三类或四类参数,模型参数,数据参数,训练参数,额外参数。 下面分别针对这四种参数进行说明。 欢迎关注公众号 模型参数 model_type模型类型,例如bloom,llama,baichuan,qwen等。 model_name_or_path模型名称或者路径。 tokenizer_name_or…

JS数组常用的20种方法详解(每一个方法都有例子,超全面,超好理解的教程,干货满满)

目录 1.会改变原数组的方法&#xff08;7种&#xff09; 1.push() 2.pop() 3.unshift() 4.shift() 5.reverse() 6.sort() 7.splice() 2.不改变原数组的方法&#xff08;13种&#xff0c;返回的新数组是从原数组浅拷贝来的&#xff09; 1.concat() 2.join() 3.slice…

12个最佳WordPress投票插件

您是否正在为您的网站寻找WordPress投票插件&#xff1f; WordPress投票插件可让您轻松地在您的网站上进行民意调查&#xff0c;用户可以投票。这是在收集见解的同时建立用户参与度的有效策略。 在本文中&#xff0c;我们精心挑选了最好的WordPress投票插件&#xff0c;可帮助…

代码随想录算法训练营第五十二天|300.最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 300.最长递增子序列 class Solution: # 2516 ms, faster than 64.96%def lengthOfLIS(self, nums: List[int]) -> int:n len(nums)dp [1] * n…

从Discord的做法中学习 — 使用Golang进行请求合并

正如你可能之前看到的&#xff0c;Discord去年发布了一篇有价值的文章&#xff0c;讨论了他们成功存储了数万亿条消息。虽然有很多关于这篇文章的YouTube视频和文章&#xff0c;但我认为这篇文章中一个名为“数据服务为数据服务”的部分没有得到足够的关注。在这篇文章中&#…

QT项目移植到VS+QT(RTI-DDS)

QT中.pro文件中include(./xxx.pri) pri文件如下定义 unset(FILENAMES)for(FILENAME, FILENAMES) {HEADERFILE $$PWD/$${FILENAME}.hif(exists($$HEADERFILE)) {HEADERS * $$HEADERFILE}SOURCEFILE $$PWD/$${FILENAME}.cppif(exists($$SOURCEFILE)) {SOURCES * $$SOURCEFILE}…

CSS-鼠标属性篇

属性名&#xff1a;cursor 功能&#xff1a;设置鼠标光标的样式 属性值&#xff1a; pointer&#xff1a;小手move&#xff1a;移动图标text&#xff1a;文字选择器crosshair&#xff1a;十字架wait&#xff1a;等待help&#xff1a;帮助 eg.html{ cursor: wait;}(此处使用css改…

SpringBoot——MVC原理

优质博文&#xff1a;IT-BLOG-CN 一、SpringMVC自动配置 SpringMVC auto-configuration&#xff1a;SpringBoot自动配置好了SpringMVC。以下是SpringBoot对SpringMVC的默认配置&#xff1a;[WebMvcAutoConfiguration] 【1】包括ContentNegotiatingViewResolver和BeanNameView…

Keil工程打开发现目标芯片无法选择解决方案

买了一个开发板&#xff0c;配套有一些底层驱动的例程&#xff0c;打开后发现目标芯片无法选择&#xff0c;对应的下载Flash FLM文件也无法选择。从提示框中可以知道所提供的例程是Keil4的例程&#xff0c;我电脑上安装的Keil版本是Keil版本&#xff0c;估计是这个原因导致工程…

C# 执行Excel VBA宏工具类

写在前面 在Excel文档的自动化处理流程中&#xff0c;有部分值需要通过已定义的宏来求解&#xff0c;所以延伸出了用C# 调用Excel中的宏代码的需求。 首先要从NuGet中引入Microsoft.Office.Interop.Excel 类库 using Excel Microsoft.Office.Interop.Excel; 代码实现 /// &l…

HashMap,1.7与1.8的区别,HashMap的扩容方式有哪些

HashMap,1.7与1.8的区别 底层数据结构的区别 JDK 1.8之前&#xff1a; 1&#xff09;JDK1.8 之前HashMap 底层是数组和链表结合在一起使用也就是链表散列。 2&#xff09;HashMap 通过key 的hashCode 经过扰动函数处理过后得到hash 值&#xff0c;然后通过(n - 1&#xff09…

修改el-radio-group样式,自定义单选组件

修改el-radio-group样式,自定义单选组件 自定义组件 MyRadioGroup.vue <template><div class"btnsBox"><el-radio-group v-model"activeIndex" change"handleClick"><el-radio-buttonv-for"(item, index) in list&qu…

CSS3动画

在CSS3中新增了一个很有意思的东西&#xff0c;那就是动画&#xff0c;有了动画我们可以做很多的事情&#xff0c;让我为大家介绍一下动画吧&#xff01; 本篇文章关于介绍动画&#xff0c;利用小球移动为你们介绍一下动画 默认样式&#xff1a; <!DOCTYPE html> <ht…

普通话考试相关(一文读懂)

文章目录&#xff1a; 一&#xff1a;相关常识 1.考试报名时间 2.报名地方 费用 证件 3.考试流程 4.普通话等级说明 二&#xff1a;题型 三&#xff1a;技巧 1.前三题 2.命题说话 四&#xff1a;普通话考试题库 1.在线题库 2.下载题库 一&#xff1a;相关常识 …

JavaEE(SpringMVC)期末复习

文章目录 JavaEE期末复习一、单选题&#xff1a; JavaEE期末复习 一、单选题&#xff1a; 1.Spring的核⼼技术是&#xff08; A &#xff09;&#xff1f; A依赖注入 B.JdbcTmplate C.声明式事务 D.资源访问 Spring的核心技术包括依赖注入&#xff08;Dependency Injection&am…

【前端】js通过canvas获取浏览器的唯一指纹可以当做唯一标识

【前端】js通过canvas获取浏览器的唯一指纹可以当做唯一标识 <!DOCTYPE html> <html><head> <meta charset"utf-8" /> <meta name"viewport" content"widthdevice-width" /> <title>JS Bin</title> &…

解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持)

目录 Reder版本2019.x Reder版本2021.1.5x Reder版本2019.x 解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持) Reder版本2021.1.5x 解决Emmy Lua插件在IDEA或 Reder 没有代码提示的问题(设置文件关联 增加对.lua.txt文件的支持)…

java游戏制作-王者荣耀游戏

一.准备工作 首先创建一个新的Java项目命名为“王者荣耀”&#xff0c;并在src下创建两个包分别命名为“com.sxt"、”com.stx.beast",在相应的包中创建所需的类。 创建一个名为“img”的文件夹来储存所需的图片素材。 二.代码呈现 package com.sxt;import javax.sw…

Netty Review - 探索ByteBuf的内部机制

文章目录 概念ByteBuf VS Java NIO BufferByteBuf实现类HeapByteBuf vs DirectByteBufPooledByteBuf vs UnpooledByteBuf其他 ByteBuf的实现机制 概念 ByteBuf是Netty中用于处理二进制数据的缓冲区 Netty的ByteBuf是一个可用于高效存储和操作字节数据的数据结构。与传统的Byt…