虚拟机把描述类的数据从Class文件加载到内存,并对数据进行验证、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。与编译时需要进行连接工作的语言不同,java语言里,类型的加载、连接和初始化过程都是在程序运行期间完成的。
一:类加载的时机
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载7个阶段。其中验证、准备、解析3个部分统称为解析。
1.1 类加载的时机
遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象,读取或者设置一个类的静态字段,已经调用一个类的静态方法时候。
使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发父类的初始化。
当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle是实类最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法所对应的类没有进行过初始化,则需要先触发其初始化。
二.类加载的过程
2.1 加载:在加载阶段,虚拟机需要完成3件事情
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据区。
- 在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口。
2.2 验证:
2.3 准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这里说的初始值说的是数据类型的零值。
2.4 解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
2.5 初始化:类初始化阶段是类加载过程的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的java程序代码。
初始化阶段是执行类构造器<clinit>()方法的过程。该方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合成的。收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。虚拟机会保证子类的<clinit>方法执行之前,父类的<clinit>方法已经执行完毕。
总结:对象初始化顺序为:静态变量或静态代码块(取决于谁先谁后)、代码块、构造器方法。