总览

先看图,这是类的生命周期,共分为七个阶段,其中验证、准备、解析三个部分统称为连接。加载、验证、准备、初始化和卸载这五个阶段顺序是确定的,但是解析在某些情况下可以在初始化之后再开始,也就是说在某些情况下需要立即进行初始化操作,当然,加载、验证、准备这三个肯定是在它之前开始的。
以下六种情况需要立即对类进行初始化:
遇到new、getstatic、putstatic或invokestatic这四条字节码指令时
使用java.lang.reflect包的方法对类型进行反射调用时
初始化类时若发现其父类还没有初始化,则需先触发其父类的初始化
当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先初始化这个主类
当使用jdk 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化
当一个接口定义了默认方法(default关键字)时,若该接口的实现类发生初始化,则该接口要在其之前初始化
简单总结就是接口和父类如果没有初始化,先初始化他们,反射调用先初始化。
加载
加载(Loading)阶段主要做三件事:
通过类的全限定名获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口
验证
验证(Verification)的目的是确保Class文件的字节流中包含的信息是否符合要求,是虚拟机保护自身的一项重要措施,大致包含四个环节:
文件格式验证,验证文件是否符合Class文件格式,如魔数、版本号、常量池等
元数据验证:对字节码描述的信息进行语义分析,保证其符合要求,如父类是否继承了不允许继承的类、抽象方法是否在抽象类中等等,主要对类进行校验
字节码验证:主要通过数据流分析和控制流分析,确定程序语义是否合法、符合逻辑,这个阶段主要是对类的方法体进行校验
符号引用验证:这个阶段可以看作是对类自身意外的各类信息进行匹配性校验,如符号引用中的全限定名是否能找到对应的类、符号引用中的类、字段和方法的可访问性是否可被当前类访问等等
准备
准备(Preparation)阶段是正式为类中定义的变量分配内存并设置变量初始值的阶段,但内存分配的仅包含类变量而不包含实例变量,实例变量是在对象实例化时随对象一起分配到Java堆中的,其次准备阶段赋予的初始值是数据类型的零值而不是代码中赋予的值,真正赋值是在初始化阶段
解析
解析(Resolution)阶段是将Java虚拟机将常量池中的符号替换为直接引用的过程,这个动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7类符号引用进行。符号引用(Symbolic References)是以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,直接引用(Direct References)是可以直接指向目标的指针、相对偏移量或一个能间接定位到目标的句柄,和虚拟机实现的内存布局直接相关的,同一个符号引用在不同的虚拟机上翻译出来的直接引用一般不会相同
初始化
初始化(Initialization)阶段会将在准备阶段已经赋过一次系统要求的初始零值的变量根据程序员通过程序编码指定的主观计划去初始化类的变量和其他资源,即执行类构造器clinit()方法的过程,包括所有类变量的赋值动作和静态语句块中的语句,这个方法同样遵循父在子先的原则,因此静态语句块中的语句,同样也是父类先执行