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
}
}
为什么我被允许在课堂上使用,但不能直接使用?
什么时候定义?
控制前向引用类变量的精确规则在JLS的§8.3.2.3部分中描述:
8.3.2.3 Restrictions on the use of Fields during Initialization
成员声明只有在成员是类或接口C的实例(分别为
static
)字段并且满足以下所有条件时才需要以文本方式显示:
- 用法发生在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; }
即使用于测试的构造函数(§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)。
我假设通过使用类,编译器将推迟查找变量,直到类完成,因此它找到y,但如果你只是像注释一样定义它尚未定义,那么它会失败
静态变量是在类加载期间按类声明的顺序定义的。当JVM加载Main
类时,将定义x
,然后y
。这就是为什么你不能在初始化y
时直接使用x
,你创建一个叫做前向引用的东西,你引用一个当前未定义的变量,这对编译器来说是非法的。
使用Main.y
时,我认为会发生以下情况:
Main
,调用x
初始化x
定义为等于Main.y
时,编译器会看到对类的引用,因此它将结束将x
定义为类y
的成员Main
的当前值。它对待这种情况就好像Main
是一个不同的阶级。请注意,在这种情况下,在初始化x
时,暂时没有定义y
。因此,x
将具有0
的值。
你不被允许这样做,因为它毫无意义。唯一可能的解释是y初始化为零,你已经有两种说法。你不需要这个。
也许编译器创建静态变量的引用,其中默认值与堆栈中的类一起创建,然后分配提供的值。