Java类加载机制中,规定了有且仅有5种情况必须立即对类进行初始化(而加载,验证,准备自然再次之前):
1. 遇到new,getstatic,putstatic和invokestatic这4条指令时,如果类没有初始化时,必须初始化类。四条指令对应我们日常所见的 使用new关键字实例化对象,读取一个类的静态字段,设置一个类的静态字段(被final修饰的静态字段除外,因为已在编译期把结果放入常量池中了)和调用一个类的静态方法。

2. 对类进行反射调用时。

3. 当初始化一个子类时,发现其父类没有初始化,则需先出发其父类的初始化。

4. 当虚拟机启动时,一个类包含main()方法时,当前类需要初始化

5. 但是用动态语言支持时,如果一个java.lang.invoke.MethodHandle实例后解析结果REF_putStatic,REF_getStatic,REF_invokeStatic的方法句柄时,当改方法句柄对应的类没有初始化时,需要初始化该类。(动态语言支持详情请查看)

初次之外所有引用类的地方都不会出发类初始化,被称为被动应用,下面举三个例子:

1. 通过子类应用父类静态字段,不会导致子类初始化,代码如下:

package com.sun.jojo.noinitclass;
public class SuperClass {
    static {
        System.out.println("SuperClass Init !!!");
    }
    public static int value = 123;
}

package com.sun.jojo.noinitclass;
public class SubClass extends SuperClass{
    static {
        System.out.println("SubClass Init !!!");
    }
}

package com.sun.jojo.noinitclass;
public class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}

结果:
SuperClass Init !!!
123

结果只输出了”SuperClass Init !!!”, “SubClass Init !!!”没有出来,对应静态字段,只有直接定义改字段的类才会初始化。

2 .通过数组定义来引用类,不会出发此类的初始化,代码如下:

package com.sun.jojo.noinitclass;
public class NotInitialization {
    public static void main(String[] args) {
        //System.out.println(SubClass.value);
        SuperClass[] superClasses = new SuperClass[10];
    }
}

运行结果并没有打印出”SuperClass Init !!!”,这里并没有出发com.sun.jojo.noinitclass.SuperClass类的初始化.这里触发了另一个名为“[Lcom.sun.jojo.noinitclass.SuperClass”的类的初始化,他是虚拟机自动创建的,直接继承于java.lang.Object的子类,创建动作由字节码指令newarray触发。

3.常量在编译期间就会调入类的常量池中,本质上没有直接引用的定义变量的类,因此使用常量字段时不会触发累的初始化。

public class ConstClass {
    static {
        System.out.println("ConstClass Init !!!");
    }
    public static final int value = 123;
}

public class NotInitialization {
    public static void main(String[] args) {
        //System.out.println(SubClass.value);
        //SuperClass[] superClasses = new SuperClass[10];
        System.out.println(ConstClass.value);
    }
}

结果:只打印出123 “ConstClass Init !!!”并没有打印出来。

接口的初始化和类的初始化类似,区别在于5种中的第三种:当一个子类的初始化过程中需要去报其父类也必须先初始化,但接口初始化时不要求其父接口也进行初始化,只有在用到父接口时,才去初始化。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐