在创建JAVA对象时,静态代码块何时执行?

7
class DemoClass {
    public static void main(String args[]) {
        System.out.println("Start");
        A a=new D();
    }
}
class A {
    static {
        System.out.println("Static A");
        A c=new C();
    }

    public A() {
        System.out.println("Constr A");
    }
}

class B extends A {
    static {
        System.out.println("Static B"); 
    }

    public B() {
        System.out.println("Constr B");
    }
}

class C extends B {
    static {
        System.out.println("Static C");
    }

    public C() {
        System.out.println("Constr C");
    }
}

class D extends C {
    static {
        System.out.println("Static D");
    }

    public D() {
        System.out.println("Constr D");
    }
}

以上代码的执行顺序如下:
Start
Static A
Constr A
Constr B
Constr C
Static B
Static C
Static D
Constr A
Constr B
Constr C
Constr D

在我看来,所有静态块都应该先执行,然后才能创建对象。但是,在这里,类A中的对象“ A c = new C()”在静态块之前创建,然后其他静态块被执行。为什么?

4
顺便说一句,我认为你可以用三个类或者可能只用两个类来演示这个问题,而且同样容易 - 这样可以更容易地提供一个全面的事件列表。 - Jon Skeet
3个回答

13
所有类的静态初始化程序都已经开始执行了 - 但是为了初始化D,必须先初始化C,因此B必须初始化,因此A必须初始化。在执行A中的静态初始化程序代码时,所有涉及的类都处于“正在初始化”的状态。
A的静态初始化程序中,它构造了C的一个实例 - 但是C已经在初始化了,因此初始化程序不会重新开始...JVM只是注意到它已经在同一线程中被初始化,然后继续执行。
这些内容的详细信息可以在JLS 12.4.2中找到。特别是这个项目符号:
如果CClass对象表明当前线程正在初始化C,则这必须是对初始化的递归请求。释放LC并正常完成。
以及
接下来,如果C是一个类而不是一个接口,并且它的超类尚未被初始化,那么让SC成为它的超类,让SI1,...,SIn成为声明至少有一个默认方法的C的所有超级接口。[...] 对于列表中的每个S [SC,SI1,...,SIn],对S递归执行整个过程。如果需要,先验证和准备S。
这些都是相关的内容。

我对在“Static B”之前的“Constr B”感到困惑。你有什么建议吗? - Mikey
1
@Mikey:完全一样 - 它是构建 C 的一部分,而这发生在 A 的静态初始化器的主体执行期间。 - Jon Skeet
@Jon Skeet,您能帮我理解您回答中引用的jls语句中的“..and complete normally”短语吗?这是一个非常古老的线程,但不知何故我仍然无法理解。 - Mayank Madhav
@MayankMadhav:“正常完成”意味着在没有异常的情况下完成(与“突然完成”相对)。 - Jon Skeet

5
@JonSkeet已经用技术术语讲解了一切,我也无法再多说。让我试着用一个类比来解释:
将静态初始化看作打开一个房间的门,构造函数执行则是在这个房间里进行/完成事情。
现在,要打开D房间的门,您需要先打开C房间的门,C房间需要B房间的门,而B房间需要A房间的门。现在,您在A房间,正在完成A房间门的开启手续。当您完成A房间门的开启手续时,您会看到一张纸条,上面写着完成C房间的工作(A c=new C();)。现在,由于C房间及其依赖房间已经打开,因此您不需要再次打开它们(也就是没有静态块初始化)。但是,在前往C房间之前,您将完成A房间的开启手续,即System.out.println("Static A");。因此,在控制台上,您会看到:

Static A

现在,您在C房间,并且必须完成此房间的工作,但是在此之前,您必须完成B房间和A房间的工作,因为它们有依赖关系(C扩展B,B扩展A)。因此,在控制台上,您会看到:

Constr A Constr B Constr C

现在,您将再次回到A房间,并发现门的开启手续已经完成。然后,您将来到B房间、C房间和D房间。因此,在控制台上,您会看到:

Static B Static C Static D

现在,您正在完成D房间的工作(A a=new D();),为此,您需要完成C房间、B房间和A房间的工作,因为它们有依赖关系(D扩展C,C扩展B,B扩展A)。因此,在控制台上,您会看到:

Constr A Constr B Constr C Constr D


-2

当JVM启动时,所有静态成员将被扫描并为它们分配内存,然后它将进行非静态操作。

因此,首先它将打印静态内容,然后是非静态内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接