静态初始值设定项在构造函数之后运行,为什么?

2022-09-01 17:40:14

我有2个班级:

A类:

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

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

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

B类:

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

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

我创建了一个 Main 类,它只创建新的 A:

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

我得到的输出是:

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

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

我知道它与我创建的循环依赖关系有关,但我的印象是静态初始值设定项应该始终在构造函数之前运行。

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

是否建议一起避免静态初始值设定项?


答案 1
static B b = new B();

在之前

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

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

初始化 B 类意味着您需要创建一个 A 实例。因此,在构造 A 实例之前,无法打印“A 静态块”。

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

请注意规范中的警告:

因为 Java 编程语言是多线程的,所以类或接口的初始化需要仔细同步,因为其他一些线程可能同时尝试初始化同一类或接口。还有一种可能性是,作为类或接口初始化的一部分,可以递归方式请求类或接口的初始化;例如,类 A 中的变量初始值设定项可能会调用不相关的类 B 的方法,而该方法又可能调用类 A 的方法。Java 虚拟机的实现负责使用以下过程来处理同步和递归初始化 [文档继续执行完整的过程]

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


答案 2