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()”,然后执行其他静态块。为什么呢?
答案 0 :(得分:13)
所有类的静态初始化程序都已启动执行 - 但为了初始化D,必须初始化C,因此必须初始化B,因此必须初始化A。在执行A
中的静态初始化程序中的代码时,所涉及的所有类都处于“正在初始化”状态。
在A
的静态初始值设定项中,它构造了C
的实例 - 但C
已经已初始化,因此初始值设定项无法启动再次... JVM只是注意到它已经被初始化(在同一个线程内)并继续。
所有这些的详细信息都在JLS 12.4.2中。特别是子弹:
如果
Class
的{{1}}对象表示当前线程正在为C进行初始化,那么这必须是初始化的递归请求。发布C
并正常完成。
和
接下来,如果C是一个类而不是一个接口,并且它的超类尚未初始化,那么让SC成为它的超类,让SI1,...,SIn成为C的所有超接口,声明至少一个默认值方法。 [...]
对于列表中的每个S [SC,SI1,...,SIn],递归执行S的整个过程。如有必要,首先验证并准备S.
......是相关的。
答案 1 :(得分:5)
@JonSkeet已经用技术术语说了一切,我不能说更多。让我试着用类比来解释:
将静态初始化视为打开房间的门,将构造函数执行视为在该房间中执行/完成操作。
现在,打开D房间的门需要打开C门,C需要B,B需要A.现在,你在A房,你在A房完成了开门手续。在A室完成开门手续时,你会看到完成C室工作的注释(A c=new C();
)。现在,由于C室及其相关房间已经打开,您不需要再打开(意味着没有静态块初始化)。但是在你去C房之前,你将完成房间的开放手续,即System.out.println("Static A");
所以,在控制台你有:
静态A
现在,你在C室,你必须完成这个房间,但在此之前你因为依赖而完成B和A(C扩展B和B扩展A)。所以,在控制台中你有:
Constr A. 建设B. Constr C
现在,您将再次回到A室,看到开门手续已经完成。所以,你会来到B室,然后是C,然后是D.所以,在控制台你有:
静态B静态C静态D
现在,你正在完成D室(A a=new D();
)的工作,为了做到这一点,你需要完成C,B和C的工作,因为依赖(D扩展C,C扩展B和B扩展A) 。所以,在控制台中你有:
Constr A Constr B Constr C Constr D
答案 2 :(得分:-2)
当jvm在那时开始运行时,将扫描所有静态成员并为他们分配内存然后它将不会静止..
所以首先它会打印静态然后非静态