上篇介绍了类的加载过程第一个阶段,加载阶段。下面将对后面几个阶段一 一分析。
验证阶段
验证阶段主要验证下几点:
1. 类文件结构的检查
2. 语义的检查
3. 字节码的验证
4. 二进制兼容性的验证
类文件结构的检查:确保类文件遵循Java类文件的格式规范,不能是
语义的检查:确保类本身符合java语言的语法固定, 比如验证final类型的类是否有子类,以及final类型的方法没有被覆盖
字节码的验证:确保字节码流可以被虚拟机安全的执行。字节码流代表Java方法(静态方法和实例方法),主要是看看逻辑上有没有错误,比如有一个跳转指令跳到了方法的外部这种。
二进制兼容性的验证:确保相互引用的类协调一致,例如在Work类的gotoWork()方法中会调用Car类的run()方法。JAVA虚拟机在验证Work类会检查方法区内是否存在Car类的run方法。假如不存在,则会抛出NoSuchMethodError错误。
准备阶段
准备阶段,Java虚拟机为类的静态变量分配内存,并设置为默认的初始值,假如对于下面的Demo类,在准备阶段,将int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0,为long类型的静态变量将其分配为8个字节的内存空间,并且赋值默认为0.
public class Demo {
public static int a=1;//准备阶段赋值为0,主动调用->初始化赋值为1
public static long b;//准备阶段赋值为0
static {
b=2;
}
}
解析阶段
在解析阶段,Java虚拟机会把二进制数据中的符号引用替换为直接引用,例如在Work中的gotoWork()方法中会引用Car类的run方法:
public void gotoWork(){
car.run();//这段代码在Work类的二进制数据中表示为符号引用
}
在Work类的二进制数据中,包含了一个队Car类的run方法的符号引用,它由run方法的全名和相关描述组成。在解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run方法在方法区的内存地址,这个指针就是直接引用。
初始化阶段
在初始化阶段,首先了解一下Java程序对类的使用方式可以分为两种:
- 主动使用
- 被动使用
主动使用情况有如下几种:
1.创建类的实例
2.访问某个类或者接口的静态变量,或者对该静态变量进行赋值
3.访问类的静态方法
4.反射(Class.forName(“com.qianyi.Demo”))
5.初始化一个类的子类
6.java虚拟机启动时被表明为启动类如常见的main启动
所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”才初始化他们。也就是符合以上6种情况之后才去初始化类。
除了以上6种情况,都算是被动使用
public class Demo {
public static int a=1;//准备阶段赋值为0,主动调用->初始化赋值为1
public static long b;//准备阶段赋值为0
static {
b=2;
}
}
对于这个Demo类,如果调用Demo.a,属于主动使用,这个时候类才开始进行进行初始化,初始化时将b=2的值赋值给b,将a的值由默认值0变为1.
后面的文章找几个例子进行演示类的加载工程