1 定义
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行验证、解析和初始化等,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制
- 类加载过程:Class文件--------java类型
- 类加载时机:运行时(编译后)
2 类的生命周期
- 加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的
- 类加载过程:加载、验证、准备、解析和初始化
3 类加载时机
java虚拟机规定了仅有以下五种情况必须立即对类进行初始化 ,因此初始化之前的动作(加载、验证、准备必须在此之前开始)
- 遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果累没有进行过初始化,则需要先触发其初始化(使用new关键字实例化对象、读取或设置一个类的静态字段、调用一个类的静态方法)
- 使用java.lang.reflect包的方法对类进行反射调用
- 初始化一个类时,如果其父类还没有进行过初始化,则需要先触发其父类的初始化
- JVM启动时,用户需要执行一个要执行的主类,JVM会先初始化这个主类
- JDK动态代理时(详细内容可查看相关资料)
4 类加载过程
4.1 加载
- 通过一个类的全限定名来获取定义此类的二进制字节流(如何获取、怎么获取随意)
- 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问接口(对HotSpot虚拟机而言,Class对象存放在方法区中)
4.2 验证
确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
4.3 准备
为类变量(被static修饰)分配内存并设置类变量初始值的阶段(方法区中分配)
- 初始值通常情况下为数据类型的零值
- 如果类字段的字段属性表中(Class文件结构中的知识,可查阅相关资料)存在ConstantValue属性,在准备阶段变量会被初始化为ConstantValue属性所指定的值
- 局部变量没有准备阶段,因此如果局部变量没有赋初始值是不能使用的,编译不会通过。
public static int value=123
变量value经过准备阶段,初始值是0而不是123
public static final int value=123
变量value经过准备阶段,初始值是123
4.4 解析
将常量池内的符号引用替换为直接引用
- 符号引用:可以是任何形式的字面量,与虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中
- 直接引用:可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用和虚拟机实现的内存布局相关,直接引用的目标必定已经在内存中存在
4.5 初始化
在初始化阶段,根据程序员制定的主观计划去初始化类变量和其他资源
- 初始化阶段是执行类构造器<clinit>()方法的过程(注意与<init>方法区分)
- <clinit>()方法时由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的
- 子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕,因此父类中定义的静态语句块优先于子类变量的赋值操作。(下面代码的执行结果是2)
public class Parent {
public static int A=1;
static {
A=2;
}
static class Sub extends Parent{
public static int B=A;
}
public static void main(String[] args) {
System.out.println(Sub.B);;
}
}