Java 类的 Linking 和 Initializing 简单笔记

共 3150字,需浏览 7分钟

 ·

2021-12-12 13:29

早期文章

        Java 类的生命周期分为 5 个阶段,分别是 Loading(加载)、Linking(连接)、Initializing(初始化)、Using(使用) 和 Unloading(卸载)。其中 Linking 阶段又分为 Verification(验证)、Preparation(准备) 和 Resolution(解析) 三个部分。

        大致情况如下图所示。

166b911f439fa2d7b44aa70260b1c017.webp

        一般所说的类加载指的是 Loading、Linking 和 Initializing 这几个阶段。其中,Loading 主要讨论关于 双亲委派、懒加载、自定义类加载器等相关的知识。在 Linking 的 Preparation 阶段 和 Initializing 可以了解类静态成员变量的初始化。在学习 Java 的相关基础知识时,了解内部的工作机制,要比单纯记忆相关语法规则可能更容易理解。这里,通过一个简单的代码实例来了解一下在 Preparation 阶段和 Initializing 阶段的对类中静态变量的赋初始值与初始化的过程。


一、一段没有意义的代码

        下面的代码是一段没有意义的代码,项目中也不会有如此的写法,但是通过下面的代码能够很好的解释静态变量在每个阶段时的值。

public class Linking {    public static void main(String[] args) {        System.out.println(Test.count);    }}
class Test { public static int count = 2; public static Test test = new Test();
private Test() { count ++; }}

        上面的代码,主要用来输出 Test 类中的 count 静态变量的值。从 Test 类中可以看到,定义了静态的 int 型变量 count 和 静态的 Test 类对象。在 main 中输出时,Test.count 的值是多少。这里通过编译并运行代码来进行查看结果。

180136a349dd59d79d37a7544385e1ac.webp

        在 IDEA 中的输出结果是 3。对于这个结果,能够了解在整个类加载时 Preparation 阶段和 Initializing 阶段 时的所作所为,即可了解该值为何如此输出。


二、Linking 阶段和 Initializing 阶段的所作所为

        在类加载的 Linking 阶段中,主要完成了三个步骤,分别是 Verification、Preparation 和 Resolution。其中,Verification 是用来验证 class 文件是否符合 JVM 的规范,比如文件格式校验、元数据校验、字节码校验 和 符号引用校验。在类验证通过后是 Preparation,该步骤主要为类中定义的静态成员变量分配内存,并为静态成员变量赋默认值。Resolution 用来完成符号引用到直接引用的解析,可以理解为将 class 文件的常量池的各种符号引用解析为内存地址的指针引用。

        Initializing 阶段用来完成类的初始化代码,其中包含静态成员变量的初始化。


三、解释代码

        有了上面两个阶段大体的工作内容,再来看一下代码。在代码中,定义了一个 Test 类,类中定义了两个静态的成员变量,分别是 int 型和 Test 类。在 Preparation 阶段,会为这两个静态成员变量分配空间,赋默认值。那么此时为 count 静态变量赋默认值,int 类型的默认值为 0,那么 count 的值为 0;类对象的默认值为 null,那么 test 的值为 null。在 Initializing 阶段,count 的值根据代码被初始化为 2,test 在经过构造方法的执行后,count 的值进行了自增 1 的操作,由 2 变为了 3。因此,在 main 方法中,输出 Test.count 时,它的值为 3。


四、修改代码

        修改上面的代码,调整两个静态成员变量定义的顺序,再来观察它的输出结果,代码如下:

class Test {    public static Test test = new Test();    public static int count = 2;
private Test() { count ++; }}

        接着按照上面的方式进行分析。首先,在 Preparation 阶段,test 的默认值为 null,count 的默认值为 0。接着,在 Initializing 阶段,调用 Test 的构造方法,此时 count 经过自增 1 变为了 1,再来完成 count = 2 的初始化。最后在 main 方法中输出 Test.count 时,它的值为 2。输出结果如下:

b72d0c57b9bdef8fd4154e8e5923c50a.webp


五、总结

        上面的内容中,介绍了 Java 类在加载时的部分内容,也从例子代码对其机制有了简单的了解。很多时候,语法规则细节之类的并不是能特别好的记住,有些知识也并不是完全靠记就好使的,更多的是了解技术背后的一些原理,这样对知识的掌握才能更加深刻。


2a10603d5f81c2584e088e2738e65f94.webp

公众号内回复 【mongo】 下载 SpringBoot 整合操作 MongoDB 的文档。


        之前整理的关于 Redis 的文章:

Redis | Redis 的安装

Redis | Redis 的帮助命令

Redis | Redis 命令分类

Redis | Redis 通用命令

Redis | Redis 字符串相关命令

Redis | Redis 列表相关命令

Redis | Redis 集合相关命令

Redis | Redis 有序集合相关命令

Redis | Redis 哈希相关命令

Redis | 源码阅读 —— 字符串

Redis | 源码阅读 —— 链表

Redis | Redis Pub/Sub相关命令

Redis | 管道 —— PipeLine

Redis | SpringBoot整合Redis

Redis | Redis 的事务一

Redis | Redis 的事务二

Redis | 基础数据类型应用场景

Redis | 事务源码阅读

Redis | 事物源码阅读 —— watch

Redis | 慢查询

Redis | 给接口添加缓存

Redis | Redis 也会算距离


浏览 31
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报