一、什么情况下下必须对类进行初始化?
1.遇到new,getstatic, putstatic, invokestatic这四条字节指令的时候,如果类没有进行初始化,则需要触发其初始化。这四条字节指令的常见场景:使用new实例化对象,读取或设置静态字段(如果被dinal修饰、已在编译器把结果放入常量池的静态字段除外),以及调用一个类的静态方法。场景基本与上方字节指令对应。
2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果这个类没有初始化,则初始化
3.当初始化一个类的时候,如果发现其父类没有初始化,先初始化其父类,父类初始化同上。
4.当虚拟机启动的时候,用户需要指定要执行的主类(包含main函数的类)先初始化。
5.当使用jdk1.7的动态语言支持时,如果java.lang.invoke.MethodHandle实例最后结果时EREF_getstatic, EREF_putstatic, EREF_invokestatic的方法句柄,并且其对应的类没有初始化,则初始化之。
对于这2中会触发类的初始化场景,成为对一个类的主动引用,除此之外,所有引用类的方法都不会触发类的初始化。
几种被动引用举例:
1.引用父类的静态字段,不会导致子类的初始化。
public class ClassA {
public static String a="sa";
static {
System.out.println("sA");
}
}
public class ClassB extends ClassA {
public static String b="sb";
static {
System.out.println("sB");
}
}
// 测试
public class TestAB {
public static void main(String[] args){
System.out.println(ClassB.a);
}
}
可知,最终输出结果应该是:由此可见,子类对象并没有初始化。
sA
sa
2.当引用static变量修饰的final常量时,由于常量在编译的储器就已经被加入常量池,因此,不会触发子类和父类的初始化。
见代码:
public class ClassA {
public static String a="sa";
static {
System.out.println("sA");
}
}
public class ClassB extends ClassA {
public static String final b="sb";
static {
System.out.println("sB");
}
}
// 测试
public class TestAB {
public static void main(String[] args){
System.out.println(ClassB.b);
}
}
输出结果:sb
4.但是是不是所有的被final修饰的常量都是这样子的结果?答案当然是No!,这个常量必须是编译期常量,下面有个例子?
public class ClassA {
public static String a="sa";
static {
System.out.println("sA");
}
}
public class ClassB extends ClassA {
public static String final b= new String("sb");
static {
System.out.println("sB");
}
}
// 测试
public class TestAB {
public static void main(String[] args){
System.out.println(ClassB.b);
}
}
输出结果:
sA
sB
sb
由此可见,首先对ClassB初始化的时候,发现父类没被初始化,先初始化老爸,再儿子!
3.通过数组定义来引用类,不会触发类的初始化
public class ClassA {
public static String a="sa";
static {
System.out.println("sA");
}
}
public class ClassB extends ClassA {
public static String b="sb";
static {
System.out.println("sB");
}
}
// 测试
public class TestAB {
public static void main(String[] args){
// System.out.println(ClassB.b);
ClassB[] classBS = new ClassB[100];
}
}
输出结果为空,以为设ClassA,ClassB都没有被初始化!
接下来看几道相关的题:
题目1:
public class Main {
public static void main(String[] args) {
System.out.println("A");
new Main();
new Main();
}
public Main() {
System.out.println("B");
}
{
System.out.println("C");
}
static {
System.out.println("D");
}
}
以上程序输出的结果,正确的是?
答案:
DACBCB
解析:
- main所在的类最先初始化执行静态块代码:D
- main函数输出:A
- 实例化Main类,由于Main类已经被初始化。而静态代码块旨在类初始化的时候运行一次。同时构造代码块优先于构造函数执行:CB
- 同上输出:CB