初始化彼此引用的静态字段时出错

时间:2012-03-14 18:14:19

标签: java

我认为我的一些代码中发现了一个错误,尽管在所有情况下它都没有出现问题。我希望比我更聪明的人可以明确地说“是的,这是一个错误”,更好的是,建议另一个替代我的实现。

我认为错误的来源是如何初始化两个类的静态字段;通过引用另一个字段来初始化一个(在FooClass中),并通过创建类型为MyUtility的对象来初始化一个(在Foo中)。抱歉,听起来不对劲;解释从来都不是我的强项。

我花了大部分时间试图减少问题,并且有一些可以运行的东西似乎可以证明这个问题。

public class Tester {

    static class FooClass {
        static final FooClass ITS_FOO = MyUtility.MY_FOO;
    }

    static class MyUtility {
        static final FooClass MY_FOO = new FooClass();

        static FooClass create() {
            return new FooClass();
        }
    }

    public static void main(String[] args) {
        System.out.println("utility's: " + MyUtility.create()); // Line "A"
        System.out.println("class's:   " + FooClass.ITS_FOO);   // Line "B"
    }
}

我意识到这个设计看起来很奇怪,但不会试图证明它的真实性(真正的代码也是“奇怪的”结构,但在具有不同可见性的单独类中等)。我肯定会感谢有关更好方法的建议。

问题的要点(至少在这个程序中)是当行B执行时FooClass.ITS_FOO字段是null。如果我切换A行和B行的顺序,则这两个字段都不是null

我见过像In what order do static initializer blocks in Java run?这样的问题,但是Java Language Spec和{{3}}似乎都没有描述这种相互参照初始化是如何完成的。

不幸的是,这个示例远离我们的实际实现,我可能会花费相同的时间来翻译任何解决方案,但是值得一些解释。

1 个答案:

答案 0 :(得分:4)

是的,如果您先强制FooClass初始化,那么会触发MyUtility初始化。 MY_FOO的初始化程序将继续,因为FooClass 已在此主题中初始化,因此MY_FOO将为非空。

另一方面,如果您从ITS_FOO构造函数(当前只是默认值)中观察FooClass,那么您会看到它在那里为空...

此行为 在规范中有详细记录 - 您链接到的部分提供了所有详细信息 - 但基本上,非常糟糕的主意有两种类型静态初始化器相互引用。不要试图以微妙的方式解决它:摆脱依赖。我意识到这可能是一种痛苦,但实际上,不值得考虑任何其他修复。

执行修复的一种方法可能是使用静态初始化程序提取第三种类型,该静态初始化程序不依赖于任何其他类型,以及其他两种类型类型可以依赖。

当然,在静态初始化程序中少做一些也很有用:)