一个类从准备加载到虚拟机的内存中,到从虚拟机中内存卸载这样的一个生命周期中,有以下几个过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 ->卸载。
在《JVM虚拟机规范》中,并没有对加载的时机做严格控制,但是对初始化的时机做了严格的控制。在六种情况下,虚拟机必须对一个类做初始化操作。
其中有一种情况是:如果读取或者写入一个静态资源,那么这个静态资源所在的类应该被提前加载。
但存在一些边界条件在这类情况中:
- 一个子类继承了父类,那么也可以通过子类去读取父类的静态资源,此时子类不会被初始化。
- 静态资源被final修饰,此时这个类不会被初始化。
第一类情况:
public class ParentClass {
static {
System.out.println("I am father");
}
public void test() {
System.out.println("Father tesst");
}
public static int value = 127;
}
--------
public class SubClass extends ParentClass{
static {
System.out.println("I am sub");
}
}
--------
public class TestMain {
public TestMain() {
}
public static void main(String[] argv) {
System.out.println(SubClass.value);
}
}
我们认为一个类被加载入虚拟机中,他首先会执行这个类中的静态方法。那么我们可以通过观察一个类的静态方法有没有被执行来判断这个类有没有被初始化。
按照示例代码运行主函数,发现输出结果为
I am father
127
子类没有被初始化,我们认为如果子类没有覆盖父类的值,那么这个静态资源应该归属父类。
第二种情况,我们将静态资源 value 用final修饰
public static final int value = 127;
重新运行代码,发现输出结果中少了静态方法执行的输出
127
查看主函数所在的编译文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
public class TestMain {
public TestMain() {
}
public static void main(String[] argv) {
System.out.println(127);
}
}
发现直接被127这个值所代替,所以我猜测是常量池资源不属于类的资源,是在编译阶段就被优化了,所以不涉及到加载类静态资源的问题。
|