【问题标题】:Java Cyclomatic Enum initialization - what happens & why does it happen? [duplicate]Java Cyclomatic Enum 初始化 - 会发生什么以及为什么会发生? [复制]
【发布时间】:2011-11-10 08:13:36
【问题描述】:

可能重复:
Java Enums: Two enum types, each containing references to each other?

在我们的代码中,我们得到了一些关于枚举的奇怪 NPE。当我搜索时,我发现(或多或少)以下案例:

public class EnumTest {

  public static void main(final String[] args){
    System.out.println("------ START ----- ");
    System.out.println("BeezleBubs FOO's rockSteady is: " + BeezleBub.FOO.rockSteady);
    System.out.println("RockSteady BAR's beezleBub is: " + RockSteady.BAR.beezleBub);
    System.out.println("------  END  ----- ");
  }

  public enum RockSteady {
    BAR(BeezleBub.FOO);

    public final BeezleBub beezleBub;
    private RockSteady(final BeezleBub beezleBub) {
        this.beezleBub = beezleBub;
        System.out.println("Constructing RockSteady, beezleBub = " + beezleBub);
    }

  }

  public enum BeezleBub {

    FOO(RockSteady.BAR);

    public final RockSteady rockSteady;

    private BeezleBub(final RockSteady rockSteady) {
        this.rockSteady = rockSteady;
        System.out.println("Constructing BeezleBub, rockSteady = " + rockSteady);
    }

  }
}

由于某种原因,结果很尴尬。运行时,此测试输出:

------ START ----- 
Constructing RockSteady, beezleBub = null
Constructing BeezleBub, rockSteady = BAR
BeezleBubs FOO's rockSteady is: BAR
RockSteady BAR's beezleBub is: null
------  END  ----- 

另一件事是,当您切换调用枚举的 System.out.prinln() 语句时,枚举的初始化也会发生变化。结果:

------ START ----- 
Constructing BeezleBub, rockSteady = null
Constructing RockSteady, beezleBub = FOO
RockSteady BAR's beezleBub is: FOO
BeezleBubs FOO's rockSteady is: null
------  END  ----- 

有人对正在发生的事情有清楚的解释吗?它与状态和秩序有关,但我不能完全确定它......

【问题讨论】:

  • 嗯,同意这个骗局... :)

标签: java static enums initialization


【解决方案1】:

在 Java 中,类是惰性加载和初始化的。这意味着,无论您尝试首先打印哪个类的属性,都会首先加载和初始化。一般来说,如果你有相互递归的类初始化器,那么你应该避免在所有构造函数完成之前检查它们的属性。

原因很简单,Java 没有可以执行的初始化步骤序列来确保所有可能使用的原子初始化。

顺便说一句,这与普通的旧 Java 类可能发生的枚举无关。

【讨论】:

  • Tim 的链接骗子有类似的解释 - 听起来都很合乎逻辑:“我明白为什么会发生 - JVM 开始类加载 Foo;它在 Foo.A 的构造函数中看到 Bar.Alpha,所以它开始类加载 Bar. 它在 Bar.Alpha 的构造函数的调用中看到了 Foo.A 引用,但是(因为我们仍在 Foo.A 的构造函数中)此时 Foo.A 为 null,因此 Bar.Alpha 的构造函数被传递了一个 null . 如果我反转两个 for 循环(或以其他方式在 Foo 之前引用 Bar),输出会发生变化,因此 Bar 的值都是正确的,但 Foo 的值不是。”
【解决方案2】:

循环依赖并不酷,我必须承认我还没有生成这样的代码。我可以猜到,实例枚举实例按需初始化,第一个实例 pocked 拉取仍然为空的最终值(最终意味着您不能重新分配值,而不是它已经被分配)。当您戳第二个枚举时,第一个已经创建并且一切都按预期工作

【讨论】:

    【解决方案3】:

    第一个 println 语句需要 BeezleBub.FOO.rockSteady 的值。所以 BeezleBub 枚举被加载。要初始化 BeezleBub FOO 实例,它需要加载 RockSteady 枚举。因此加载了 RockSteady,并将 BAR 常量初始化为 BeezleBub.FOO 的当前值,该值仍然为空,因为它正在初始化自身。然后将初始化的 BeezleBub.FOO 常量赋值为 RockSteady.BAR 的值,该值非空。

    这是一个有趣的问题,因为它是枚举之一应该是可变的以便正确初始化的情况之一。

    【讨论】:

    • 阅读你的 cmets 让我觉得测试用例有点令人困惑,因为在 sysout 完成之前调用了 BeezleBub.FOO.rockSteady。给人的印象是枚举实际上是在其他任何东西之前实例化的......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多