[TOC]
类加载过程
类加载时机
- 使用new关键字实例化对象
- 使用一个类的静态字段(不包括静态常量)和静态方法。
- 使用类的反射。
- 初始化一个类的时候,自动触发父类的初始化。
- 虚拟机启动,初始化包含 main() 的类。
以上都是类的主动引用。
类的被动引用不会导致类的初始化:
- 通过子类访问父类的静态字段,子类不会被初始化,父类会初始化。
- 通过数组引用类,不会造成类的初始化。
- A类使用B类文件中的静态常量时,B类不会初始化。编译阶段已把静态常量存入A类的常量池中了。
类的整个生命周期
加载、验证、准备、解析、初始化、使用和卸载。其中验证、准备、解析统称为连接。
加载
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在Java内存中生成一个代表这个类的java.lang.Class对象(对于HotSpot,java.lang.Class类是存在方法区的),作为方法区这些数据的访问入口(java堆中存储着class对象类型指针和对象实例数据)。
这一阶段是程序员可控性最强的一个阶段:其可以自己选择系统提供的类加载器,也可以自己实现一个类加载器。
加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始。
连接-验证
由于class文件可以由非java编译器产生,所以验证阶段还是相当重要的。
文件格式验证
验证字节流是否符合Class文件格式规范,和版本兼容性。
- 验证魔数是否0xCAFEBABE;
- 主、次版本号是否正在当前虚拟机处理范围之内;
- 常量池的常量中是否有不被支持的常量类型
- 。。。
文件格式验证通过后,字节流被存储在方法区。
元数据验证
对字节码描述的信息进行语义校验。
- 这个类是否有父类;
- 这个类的父类是否继承了不允许被继承的类;
- 如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法
- ……
字节码验证
校验类的方法体,保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。
符号引用验证
发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在“解析阶段”中发生
连接-准备
为类变量分配内存并设置变量初始值,由于这个阶段仅分配类变量(被static修饰,不包括实例变量),所以该类变量在方法区中分配。
这个阶段的初始化,仅将类变量初始化为0.
连接-解析
-类或接口的解析
-字段解析
-类方法解析
-接口方法解析
初始化
到了初始化阶段,才真正开始执行类中定义的Java程序代码。
初始化阶段是执行类构造器<clinit>()
方法的过程。<clinit>()
方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的 。