Java-底层原理-clinit和init

Java-底层原理-初始化之clinit和init

系列文章目录

Java-底层原理-编译原理
Java-底层原理-javac源码笔记
Java-底层原理-类加载机制
Java-底层原理-clinit和init

摘要

在准备阶段,类变量(静态非final字段)被设初值,如int类型被设为0,常量被设值。

而初始化阶段是类加载的最后一步,此时才会真正开始执行java应用程序代码(字节码)。此阶段中,会真正为类变量赋初值,以及做其他资源的初始化工作。这个阶段就会执行类构造器即<clinit>方法。

0x01 <clinit>

1.1 简介

<clinit>方法是由编译器自动收集的,包括所有类变量(静态非final)赋值和静态语句块(static{})。该收集行为顺序由语句在源码文件中出现的顺序决定,具体来说,静态语句块只能访问到定义在语句块之前的变量:

public class ClinitTest1
{
    static {
        a = 1;
        // 下面这个会报错 illegal forward reference
//        int c = b;
    }
    static int a;
    static int b;

    public static void main(String[] args)
    {
        System.out.println("a=" + a);
    }
}

1.2 执行时机

类加载的初始化阶段运行<clinit>。且在执行子类前,会先执行父类的<clinit>方法。也就是说,JVM中执行的第一个clinit方法来自Object类。

注意,接口中也有<clinit>方法,但实现类初始化或子类接口执行<clinit>方法时,不会执行父接口的<clinit>。而是要等到父接口定义变量使用时才会调用clinit。

在以下情况发生时,还没有发生类过初始化,则会先触发其初始化。

  1. 对一个对象使用new操作符;
  2. Class.forName(a.b.c.Test)
  3. XXX.class.newInstance()
  4. 读取或写入一个类的静态字段(除了final static或已在编译器把变量放入常量池的静态字段);
  5. 调用一个类的静态方法时
  6. 使用java.lang.reflect包的方法对类进行反射调用的时候
  7. 当初始化一个类的时候,如果父类还没有初始化过,则需要先触发父类初始化(接口除外)
  8. 执行main方法所在的类
  9. 使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic的方法句柄,并且这个句柄所在类还未初始化过

以下不会触发初始化:

  1. Class initable4 = loader.loadClass(“demos.classInitialization.InitClass.Initable4”);
  2. System.out.println(Initable4.staticFinal);

1.3 多线程与clinit

在多线程场景下,由JVM保证只有一个线程能执行该类的<clinit>方法。在执行过程中,其他线程全部阻塞等待。等唯一的一次clinit方法完成后,所有线程开始执行自己的代码。

也就是说,一个类只会在一个JVM进程运行期间执行一次<clinit>方法。

1.4 无clinit的情况

以下类没有<clinit>方法:

  1. 没有声明任何类变量、没有static块语句
  2. 声明了类变量,但没有对应的变量初始化语句
  3. 只有final类变量,且赋值语句是常量(如 final static int danlu = 1;)。这样的赋值会在准备阶段就执行

0x02 <init>

2.1 简介

就是对象实例初始化的方法。

2.2 执行时机

对象实例化时。

实例化有4种方法:

  1. new
  2. XXX.class.newInstance()或XXX.class.getDeclaredConstructor().newInstance()
  3. 调用对象的clone方法
  4. 通过 java.io.ObjectInputStream 类的 getObject() 方法反序列化

2.3 init 方法执行顺序

  1. 父类变量初始化
  2. 父类语句块
  3. 父类构造函数
  4. 子类变量初始化
  5. 子类语句块
  6. 子类构造函数

2.4 init加clinit的执行顺序

  1. 父类静态变量初始化
  2. 父类静态语句块
  3. 子类静态变量初始化
  4. 子类静态语句块
  5. 父类变量初始化
  6. 父类语句块
  7. 父类构造函数
  8. 子类变量初始化
  9. 子类语句块
  10. 子类构造函数
    以上1-4位clinit内容。
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值