5
public class Main {

    static int x = Main.y;
//  static int x = y; //Not allowed; y is not defined
    static int y = x;
    public static void main(String[] args) {
        System.out.println(x);//prints 0
    }
}

为什么我可以在课堂上使用 y ,但不能直接使用?

y 什么时候定义?

4

5 回答 5

10

管理对类变量的前向引用的精确规则在 JLS 的第8.3.2.3节中描述:

8.3.2.3 初始化期间使用字段的限制

仅当成员是static类或接口 C 的实例(分别)字段并且满足以下所有条件时,成员的声明才需要在使用之前以文本形式出现:

  • 该用法发生在 C 的实例(分别static)变量初始化器或 C 的实例(分别static)初始化器中。
  • 用法不在作业的左侧。
  • 用法是通过一个简单的名称。
  • C 是包含用法的最里面的类或接口。

如果不满足上述四个要求中的任何一个,则会发生编译时错误。

这意味着测试程序会导致编译时错误:

  class Test {
      int i = j;  // compile-time error: incorrect forward reference
      int j = 1;
  }

而下面的示例编译没有错误:

  class Test {
      Test() { k = 2; }
      int j = 1;
      int i = j;
      int k;
  }

即使 Test 的构造函数 (第 8.8 节)引用了在三行之后声明的字段 k。

这些限制旨在在编译时捕获循环或其他格式错误的初始化。因此,两者:

class Z {
  static int i = j + 2; 
  static int j = 4;
}

和:

class Z {
  static { i = j + 2; }
  static int i, j;
  static { j = 4; }
}

导致编译时错误。方法访问不会以这种方式检查,因此:

class Z {
  static int peek() { return j; }
  static int i = peek();
  static int j = 1;
}
class Test {
  public static void main(String[] args) {
      System.out.println(Z.i);
  }
}

产生输出:

0

因为 i 的变量初始化器使用类方法 peek 在 j 被其变量初始化器初始化之前访问变量 j 的值,此时它仍然具有其默认值(第 4.12.5 节)

于 2010-09-13T09:16:17.890 回答
2

我假设通过使用该类,编译器将推迟查找变量,直到该类完成,所以它找到 y,但是如果你只是像注释一样定义它,它还没有定义,所以它失败了

于 2010-09-13T09:07:27.273 回答
1

静态变量在类加载期间按照类中的声明顺序定义。当 JVM 将加载Main类时,x将定义,然后y. 这就是为什么你不能y在初始化时直接使用x,你创建了一个叫做前向引用的东西,你引用了一个当前未定义的变量,这对编译器来说是非法的。

使用时Main.y,我认为会发生以下情况:

  • 你加载Mainx初始化被调用
  • 当您定义x为等于时Main.y,编译器会看到对类的引用,因此它将结束对类x成员的当前值的定义。它将这种情况视为不同的类。yMainMain

请注意,在这种情况下,在初始化时xy暂时还没有定义。所以x会有一个值0

于 2010-09-13T09:13:24.080 回答
0

你不被允许这样做,因为它没有意义。唯一可能的解释是y被初始化为零,并且您已经有两种说法。你不需要这个。

于 2010-09-13T09:07:08.710 回答
0

也许编译器在创建静态变量时使用堆栈中的类创建具有默认值的引用,然后分配提供的值。

于 2010-09-13T09:22:55.747 回答