Java中类初始化的奥秘

程序员小迷 2024-06-08 11:31:50

一、概述

在 Java 语言中,类的加载、链接(验证、准备、解析)和初始化过程都是在程序运行期间完成的。

其中加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的。解析阶段可以在初始化之前也可以在初始化之后再开始(又叫作运行时绑定、动态绑定、晚期绑定)。

二、以下5种情况必须对类进行初始化(当然加载、验证、准备阶段也需要在初始化之前完成):

1.遇到 new、getstatic、putstatic 或 invokestatic 这 4 条字节码指令时若类未初始化则触发初始化。

使用场景:使用 new 关键字实例化对象、读取一个类的静态字段(被 final 修饰并且已在编译期把结果存入常量池的静态字段除外)、调用一个类的静态方法等。

2.使用 java.lang.reflect 包中的方法对类进行反射调用的时候。

3.当初始化一个类的时候,如果其父类还未进行初始化,则需先触发其父类的初始化。

4.当虚拟机启动时,用户需指定一个要加载的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类。

5.当使用 JDK 1.7 的动态语言技术时,若一个 java.lang.invoke.MethodHandle 实例最终的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄时,并且这些方法句柄所对应的类未进行过初始化,则需先触发这些类的初始化。

三、以上5种方式是对一个类的主动引用,除此之外,所有其它引用类的方法都不会触发初始化,这叫做被动引用。例如:

/**

* 虚拟机在运行时动态创建了一个数组类,通过数组定义来引用类不会触发此类的初始化。

*/

AClass[] ac = new AClass[8];

/**

* 常量在编译阶段会存入调用类的常量池中,

* 所以本质上并没有直接引用到定义类常量的类,

* 因此不会触发定义常量的类的初始化。

* Constant.FOO="foo" 在编译期常量传播优化时已经存储到 常量池中了。

*/

System.out.println(Constant.FOO);

微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是程序员小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!

0 阅读:15

程序员小迷

简介:致力于Android、iOS、C、Java等编程技术的技巧经验分享