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

26

我有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");
     }
}

我创建了一个主类,只是创建了一个新的 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
谁给我点了踩,请留下评论。 - Aviram Segal
3
其实是个很好的问题,加一赞.. :) - PermGenError
@AviramSegal 有些人习惯于踩负面评价..他们从来不会读完整个问题,只看标题就踩了问题。 - Ravi
1个回答

26
static B b = new B();

之前是

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

所以你要求在打印"A static block"之前必须初始化B实例。

而初始化B类意味着需要创建一个A实例。因此,在构建A实例之前无法打印"A static block"。

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

请注意规范中的警告:

由于Java编程语言是多线程的,类或接口的初始化需要仔细同步,因为有些其他线程可能正在尝试同时初始化相同的类或接口。 还有可能作为该类或接口初始化的一部分递归请求初始化;例如,类A中的变量初始化器可能调用不相关类B的方法,而类B可能反过来调用类A的方法。 Java虚拟机的实现负责使用以下程序照顾同步和递归初始化[文档继续提供完整程序]

在Java和其他语言中,最佳实践基本上是避免循环依赖关系,因为它们的解决可能非常难以预测。


1
我可以理解错误,但为什么要允许不一致的行为?而且,我也找不到相关文档。 - Aviram Segal
无法理解“在A实例构造之前打印“A静态块”的方法。” 这个这个说:“当类被加载到内存中时执行。”难道不是在创建实例之前加载类吗?如果是,那么静态块不应该在创建实例之前执行吗?换句话说,为什么输出不是“B静态块,A静态块,A构造函数,B构造函数,A构造函数”? - MsA

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