16

我遇到了一件有趣的事情:

static {
    System.out.println(test);     // error cannot reference a field before it is defined
    System.out.println(cheat());  // OK! 
}

private static boolean cheat() {
    return test;
}

private static boolean test = true;

public static void main(String args[]) {}

第一种方法是错误的,您的编译器和 IDE 都会告诉您它是错误的。在第二种情况下,作弊是可以的,但它实际上将字段默认testfalse. 使用 Sun JDK 6。

4

3 回答 3

11

这是在JLS 8.3.2.3中定义的。尤其:

如果使用发生在 C 的 [...] 静态初始化程序中,则成员的声明需要在使用之前以文本形式出现 [...]。

当您打电话时,cheat()您会绕过该规则。这实际上是该部分示例列表中的第 5 个示例。

请注意,这cheat()将在静态初始化程序块中返回 false,因为test尚未初始化。

于 2013-01-29T16:54:00.670 回答
3

因为类加载按以下顺序工作:

  • 加载类定义(方法、签名)
  • 为静态变量引用(for test)分配内存 - 尚未初始化
  • 执行static初始化程序(用于变量)和static块 - 按它们定义的顺序

因此,当您到达static块时,您已经准备好方法定义,但没有准备好变量。cheat()你实际上是在读取一个未初始化的值。

于 2013-01-29T16:55:56.390 回答
1

这是类加载发生的一般步骤。

  1. 加载 - 将类加载到内存
  2. 验证 - 检查 e 类的二进制表示是否正确
  3. 准备 - 为类创建静态字段并将这些字段初始化为其标准默认值。
  4. 初始化 - 将为静态字段调用静态初始化器和初始化器

准备好后,您的测试将是假的。然后在您将静态变量分配为真之前,您的静态块将执行。这就是您得到假的原因。

尝试将静态变量设为最终变量。在这种情况下,您将得到真实的结果。这是因为您的编译器本身会将值嵌入字节码中(因为该字段是最终的)作为优化的一部分

于 2013-01-29T17:14:24.280 回答