类的整个生命周期包括了:加载( Loading )、验证( Verification )、准备( Preparation )、解析( Resolution )、初始化( Initialization )、使用( Using )和卸载( Unloading )七个阶段。其中验证、准备和解析三个部分统称为连接( Linking ),这七个阶段的发生顺序如下图。

深入理解JVM(3)——类加载机制

上图中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序开始。但解析阶段不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 语言的运行时绑定(也称为动态绑定或晚期绑定)。这里是按顺序“开始”,而不是“进行”或“完成”,是因为这些阶段通常都是互相交叉地混合式进行的,通常再一个阶段 执行的过程中调用或激活另外一个阶段。

 

1.1、类的初始化时机

虚拟机规范严格规定了只有四种情况(主动引用)必须对类进行“初始化”(而加载、验证、准备自然需要在此之前开始)。所有的 Java 虚拟机实现必须在每个类或接口被java程序“首次主动引用”时才初始化他们。

主动引用

  1. 遇到 new、getstatic、putstatic 或 invokestatic 这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的常见Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
  2. 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
  3. 当初始化一个类的,如果其父类还没有进行过初始化,则需要先触发其父类的初始化。
  4. 当虚拟机启动时,被标明为启动类的类(包含 main() 方法的那个类)。

被动引用

除了以上四种情况,其他使用 Java 类的方式都被看作是对类的被动使用,都不会导致类的初始化。

不属于主动使用的几种情况

1、当final 变量是基本数据类型以及 String 类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。静态变量是编译时的常量(即2.3准备阶段的中带 ConstantValue 属性的类变量),在使用这个静态变量的时候,就不会进行初始化。

深入理解JVM(3)——类加载机制
import java.util.Random;

class FinalTest {
    public static final int X = 6 / 3;
    public static final int Y = new Random().nextInt(100);

    static {
        System.out.println("FinalTest static block");
    } 
}

public class test {
    public static void main(String[] args) {
        System.out.println(FinalTest.X);    //执行该句,没有打印静态块内输出语句
        System.out.println(FinalTest.Y);    //执行该句,打印了静态块内输出语句
    }
}
final修饰的类变量作为常量

相关文章:

  • 2022-01-01
  • 2021-12-07
  • 2021-07-30
  • 2021-07-27
  • 2022-02-10
  • 2021-10-07
  • 2022-01-10
  • 2021-09-28
猜你喜欢
  • 2021-05-22
  • 2021-08-30
  • 2021-05-22
  • 2022-12-23
  • 2022-01-04
相关资源
相似解决方案