静态初始化程序在构造函数之后运行,为什么?


问题内容

我有2节课:

Class A:

public class A {
    static B b = new B();

     static {
         System.out.println("A static block");
     }

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

Class B:

public class B {
     static {
         System.out.println("B static block");
         new A();
     }

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

I create a Main class which just creates new A:

public class Main {
    public static void main(String[] args) {
        new A();
    }
}

The output I get is:

B static block
A constructor
B constructor
A static block
A constructor

如您所见,A的构造函数在其静态初始值设定项之前被调用。

我了解它与我创建的循环依赖关系有关,但我印象中静态初始化程序应始终在构造函数之前运行。

发生这种情况的原因是什么(技术上在Java实现中)?

是否建议同时避免使用静态初始化程序?


问题答案:

static B b = new B();

is before

static {
     System.out.println("A static block");
}

因此,您需要在打印之前初始化B实例”A static block”。

初始化B类意味着您需要创建一个A实例。因此,

构造A实例之前,无法打印“静态块” 。

是的,在构造函数启动之前就启动了A的静态初始化,
但是除了死锁之外,没有其他解决方案可用于
您需要的序列。

Note the warning in the
specification

:

因为Java编程语言是多线程的,所以
类或接口的初始化需要仔细的同步,因为某些其他线程
可能试图同时初始化同一类或接口。

可能会递归地请求类或接口的初始化,作为该类或接口的初始化的一部分
;例如,类A中的变量初始值设定项可能会调用
不相关的类B的方法,而后者又可能会调用类
A的方法。Java虚拟机的实现负责
通过使用同步来实现同步和递归初始化。
遵循以下步骤[文档继续执行完整步骤]

与其他语言一样,使用Java的最佳实践基本上是避免循环依赖,因为它们的分辨率可能很难预测。