关于Java类初始化的一些特性
在 Java中,虚拟机对于类的加载时机并未严格的约束而是根据各虚拟机的特性自行决定,但是对于初始化来说,存在几种情况使得类必须立即执行初始化操作:
1)当使用new,putstatic,getstatic,invokestatic这4条指令时,若该类未被初始化则需立即对该类进行初始化。
2)对类进行反射调用时,若该类未被初始化则需立即对该类进行初始化。
3)初始化一个类时,若该类存在父类且该父类未初始化则先初始化该类的父类。
4)虚拟机启动阶段,会初始化用户指定的需要执行的主类。
5)当使用Java 1.7的动态语言支持时,若一个 java.lang.invoke.MethodHandle实例最后的解析结果REF_putstatic,REF_getstatic,REF_invokestatic的方法句柄,若这个方法句柄对应的类未进行初始化则需对该类进行初始化。
上述5种情况会直接初始化对应类,接下来通过示例看下几种其他会初始化的情况:
示例1:
package testReference;
public class A {
static{
System.out.println("A class init");
}
public static String a_class = "A_CLASS";
}
package testReference;
public class B extends A {
static{
System.out.println("B class init");
}
public static String b_class = "B_CLASS";
}
package testReference;
public class Test {
public static void main(String[] args) {
System.out.println(B.a_class);
}
}
运行结果为:
这是因为对于静态变量,只有直接定义这个变量的类才会初始化,在上面的示例总,因为a_class是在A中定义的因此实际上只有A进行了初始化。
示例2:将上述test类变换下代码如下:
package testReference;
public class Test {
public static void main(String[] args) {
System.out.println(B.b_class);
}
}
输出结果为:
因为这里输出的是B中的静态变量,JVM首先会检测B,因为B有父类A所以首先检测A是否已初始化,因为A尚未初始化因此会先初始化A然后再初始化B最后输出b_class。
示例3:将A中静态变量用final修饰并在test类中输出A中变量如下:
package testReference;
public class A {
static{
System.out.println("A class init");
}
public static final String a_class = "A_CLASS";
}
最后输出结果为:
这是因为在类编译阶段,通过JVM常量传播优化,已将a_class的值传给了Test类的常量池中,因此实际对B.b_class的引用直接转为自身的常量池引用。
上一篇: 吃透Java基础三:初始化类的五种方式
下一篇: 有关初始化的问题3