一、加载阶段
首先类加载器通过不同渠道加载类,加载后Java虚拟机将字节码中的信息保存到方法区中,生成一个InstanceKlass对象,保存类的所有信息。
同时,Java虚拟机会在堆中生成一份与方法区中数据类似的java.lang.Class对象(反射时获取的对象,详见Java进阶——反射篇),作用是在Java代码中去获取类的信息以及存储静态字段的数据(JDK8之后静态数据存放在堆中)。
二、连接阶段
1.验证,验证内容是否满足Java虚拟机规范
(1)文件格式验证,是否以魔数CAFEBABE开头,及主次版本是否满足当前Java虚拟机版本要求
(2)元信息验证,如一个类必须有父类(即使程序员不继承也会默认继承java.lang.object)
(3)验证程序执行指令的语义,如方法内的指令跳转到不正确的位置
(4)验证符号引用,如是否访问了其他类中的private方法等
2.准备,给静态变量分配内存并赋初值
在堆区给静态变量分配内存并分配一个默认初始值,如int为0,boolean为false,引用类型为null等(与设定值无关,设定值发生在初始化阶段)
若该值被finnal修饰,则初始化时直接初始化为设定的值。(final相当于const,值不可改变,方法不能重写,声明的类不能被继承)
3.解析,将常量池中符号引用替换成指向内存的直接引用(即将符号引用改变为内存地址)
三、初始化阶段
执行静态代码块中的代码,并为静态变量赋值
对象数组的创建不会导致数组中的元素的类进行初始化。
final修饰的变量如果赋值的内容需要执行指令才能得出结果,会调用clinit方法进行初始化。
对于静态代码块:
静态代码块会在类的初始化阶段被执行,有多个静态代码块则按顺序执行。
无论是否创建此类的对象,只要加载、连接、初始化,静态代码块都会被调用。
非静态代码块:即无static,只有{ }里面的代码块,会被当作类的构造器的一部分执行,有多个则按顺序,且执行顺序优先于构造器。
如上图,init方法即构造方法,先执行非静态代码块,然后执行构造方法。
clinit即classinit,在类的初始化中执行的,该方法中为类中的静态代码块,按顺序执行。