Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。
关于在什么情况下需要开始类加载过程的第一个阶段“加载”,《Java虚拟机规范》中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,《Java虚拟机规范》则是严格规定了有且只有六种情况必须立即对类进行“初始化”(初始化必然要先加载)。
Java程序对类的使用分为主动引用和被动引用,针对上述六种情况,对于这六种会触发类型进行初始化的场景,《Java虚拟机规范》中使用了一个非常强烈的限定语——“有且只有”。
主动引用的六种情况:
/**
* FileName: ClassInitTest
* Author: lxg
*/
public class ClassInitTest {
public static void main(String[] args) {
// 1. 创建实例
InitTest1 it1 = new InitTest1();
// 2. 访问静态属性
// System.out.println(InitTest1.n);
// 3. 调用静态方法
// InitTest1.method();
// 4. 反射
// Class cls = Class.forName("classloder.initialization.InitTest1");
// 5. 初始化子类
// InitTest2 it2 = new InitTest2();
// 6. 启动类触发初始化,执行main()触发
// 7. 跳过
// 8. 当一个接口中的默认方法(被default关键字修饰的接口方法)初始化时,IfTestImpl初始化时会触发InitTest1 it1 = new InitTest1(),证明IfTest接口初始化
// IfTest ifTest = new IfTestImpl();
}
}
// 类初始化会执行初始化类静态属性
class InitTest1 {
static {
System.out.println("InitTest1初始化...");
}
public static int n = 10; // 静态变量
public static void method() { // 静态方法
n = 30;
}
}
class InitTest2 extends InitTest1 {
static {
System.out.println("InitTest2初始化...");
}
}
// 接口,验证第8条
interface IfTest {
InitTest1 it1 = new InitTest1();
default void method() {
}
}
class IfTestImpl implements IfTest {
static {
System.out.println("IfTestImpl初始化...");
}
}
除了上述主动引用的六种情况会触发类的初始化,其他的引用都不会触发初始化,被称为被动引用,以下是被动引用的其中几种情况举例:
public class ClassInitTest2 {
public static void main(String[] args) {
System.out.println(SubClass.m);
}
}
/**
* 被动使用类字段
* 通过子类引用父类的静态字段,不会导致子类的初始化
*/
class SuperClass{
static {
System.out.println("Super class init");
}
public final static int m = 20;
public static int n = 10;
}
class SubClass extends SuperClass{
static {
System.out.println("SubClass init");
}
}

