为什么在通过Java中的方法定义静态字段之前可以访问它?

为什么在通过Java中的方法定义静态字段之前可以访问它?,java,Java,我遇到了一件有趣的事情: 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; publ

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

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都会告诉您它是错误的。在第二种情况下,作弊是可以的,但它实际上将字段
test
默认为
false
。使用Sun JDK 6。

这是在中定义的。特别是:

如果在C的[…]静态初始值设定项中使用,则在使用[…]之前,成员的声明需要以文本形式出现

当您调用
cheat()
时,您将绕过该规则。这实际上是该部分的第五个示例


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

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

  • 加载类定义(方法、签名)
  • 为静态变量引用分配内存(对于
    test
    )-尚未初始化
  • 执行
    静态
    初始值设定项(用于变量)和
    静态
    块-按定义顺序执行

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

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

  • 加载-将类加载到内存中
  • 验证-检查类e的二进制表示是否正确
  • 准备-为类创建静态字段,并将这些字段初始化为其标准默认值
  • 初始化-将调用静态初始值设定项和静态字段的初始值设定项
  • 在准备之后,测试将为false。然后在将静态变量赋值为true之前,将执行静态块。这就是为什么会为false


    尝试将静态变量设为final。在这种情况下,您将得到true。这是因为编译器本身将在字节码中嵌入值(因为字段是final),作为优化的一部分。我一直在寻找这个参考资料,当时我获得了否决票。@Andremoniy我正在写这个。@assylias,Java 6有类似的例子吗?@mdobrinin它的工作原理是一样的-这一部分已经是Java 5的一部分,因此自Java 5以来在这方面没有任何变化。