JVM学习系列:类的加载(初始化阶段)

概述

本文仅关注类加载过程中的初始化阶段。初始化指的就是对类的静态变量进行初始化。

初始化

Java中,对类变量进行初始化有两种形式:

  • 声明类变量是赋予初始值:public static int i = 0;
  • 采用静态代码块进行赋值:static{ i = 0;}

在编译阶段,类中的类变量初始化语句和静态代码块会被按照在.java文件中出现的顺序进行收集,形成一个类初始化方法<clinit>()。在加载的初始化阶段,这个方法就会被调用,对类的静态变量进行初始化。
初始化阶段有如下规则:

  • 先父类再子类,源码中先出现先执行
  • 向前引用:一个类变量在定义前可以赋值,但是没法引用
  • 非必要:一个类或接口如果没有静态变量,那么不需要生成<clinit>()
  • 执行接口的<clinit>()方法前不需要先执行父接口的()方法,同样,执行类的<clinit>()方法前不需要执行实现接口的<clinit>()方法,只有在父接口或者被实现接口中的变量被调用时才会触发
  • 同步性:<clinit>()方法只会执行一次

初始化触发条件

类的初始化分为两种:
1.主动引用

  • 在类没有进行过初始化的前提下,当执行new、getStatic、setStatic、invokeStatic字节码指令时,类会立即初始化。对应的java操作就是new一个对象、读取/写入一个类变量(非final类型)或者执行静态方法。
  • 在类没有进行过初始化的前提下,当一个类的子类被初始化之前,该父类会立即初始化。
  • 在类没有进行过初始化的前提下,当包含main方法时,该类会第一个初始化。
  • 在类没有进行过初始化的前提下,当使用java.lang.reflect包的方法对类进行反射调用时,该类会立即初始化。
  • 在类没有进行过初始化的前提下,当使用JDK1.5支持时,如果一个java.langl.incoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

2.被动引用
非主动引用的都属于被动引用

举个例子

public class InitTest {
    private static int a = 1;//连接时的准备阶段赋值为0,初始化阶段执行赋值为1
    private static final int b = 2;//连接时的准备阶段赋值为2,初始化阶段不执行该语句
    private static int c;//连接时的准备阶段赋值为0,初始化阶段不执行该语句
    static{
        a = 10;//初始化阶段执行赋值为10
    }
    public static void main(String[] args){
        System.out.println("a:"+a);
        System.out.println("b:"+b);
        System.out.println("c:"+c);
    }
}

在初始化阶段,赋值语句的执行顺序如下:
1.private static int a = 1;
2.a = 10;
执行结果:

a:10
b:2
c:0

前向引用

在前文初始化这一段中提及了“前向引用”,在这里做一下解释。先看一下下面的代码:

public class InitTest {
    static{
        i = 10;
        System.out.println(i);//Illegal forward reference
    }
    public static void main(String[] args){
        System.out.println(i);
    }
    private static int i=1;
}

这段代码中会报错“Illegal forward reference”,原因就在于提前引用了类变量i。但是如果去掉System.out.println(i)这个代码是可以运行的,且输出结果为1
为什么这段代码中的变量i可以提前赋值但是不能提前引用呢?
原因就在于,静态代码块的执行阶段在加载的初始化,而加载的连接阶段中的准备动作会将类的静态变量初始化为零值,所以初始化阶段可以对类变量进行赋值;引用失败的原因则是在于初始化阶段顺序执行静态变量赋值和静态代码块中的代码,在上面的这个代码中,先是执行了代码块的内容再执行private static int i=1

参考

https://segmentfault.com/a/1190000012527652
https://www.jianshu.com/p/3556a6cca7e5
https://blog.csdn.net/qq_36381855/article/details/79980619

posted @ 2021-12-14 10:38  偷偷地跳槽  阅读(308)  评论(0)    收藏  举报