为什么这个Java代码会编译?
在方法或类作用域中,编译以下行(带有警告):
int x = x = 1;
在类作用域中,变量获取其默认值,下面给出“未定义的引用”错误:
int x = x + 1;
难道第一个不应该以相同的“未定义的引用”错误结束吗?或者也许第二行应该编译?还是我错过了什么?x = x = 1
int x = x + 1
在方法或类作用域中,编译以下行(带有警告):
int x = x = 1;
在类作用域中,变量获取其默认值,下面给出“未定义的引用”错误:
int x = x + 1;
难道第一个不应该以相同的“未定义的引用”错误结束吗?或者也许第二行应该编译?还是我错过了什么?x = x = 1
int x = x + 1
对于字段,是非法的,因为 是对 的非法转发引用。您实际上可以通过编写来解决此问题,该编译无需投诉。int b = b + 1
b
b
int b = this.b + 1
对于局部变量,是非法的,因为在使用前未初始化。对于始终默认初始化的字段,情况并非如此。int d = d + 1
d
您可以通过尝试编译来查看差异
int x = (x = 1) + x;
作为字段声明和局部变量声明。前者会失败,但后者会成功,因为语义上的差异。
首先,字段和局部变量初始值设定项的规则非常不同。因此,这个答案将分两部分解决规则。
我们将在整个过程中使用此测试程序:
public class test {
int a = a = 1;
int b = b + 1;
public static void Main(String[] args) {
int c = c = 1;
int d = d + 1;
}
}
的声明无效,并且失败并显示错误。
的声明无效,并且失败并显示错误。b
illegal forward reference
d
variable d might not have been initialized
这些错误不同的事实应该暗示错误的原因也不同。
Java 中的字段初始值设定项由 JLS §8.3.2 字段初始化控制。
字段的作用域在 JLS §6.3 声明的作用域中定义。
相关规则有:
m
§8.3.2.3 规定:
仅当成员是类或接口 C 的实例(分别是静态)字段并且以下所有条件都成立时,该成员的声明才需要以文本方式显示才能使用:
- 用法发生在 C 的实例(分别是静态)变量初始值设定项或 C 的实例(分别是静态)初始值设定项中。
- 用法不在作业的左侧。
- 用法是通过一个简单的名称。
- C 是包含用法的最内层类或接口。
您实际上可以在声明字段之前引用字段,但在某些情况下除外。这些限制旨在防止类似代码
int j = i;
int i = j;
从编译。Java规范说:“上述限制旨在捕获编译时循环或其他格式错误的初始化。
这些规则实际上归结为什么?
简而言之,规则基本上说,如果 (a) 引用位于初始值设定项中,(b) 引用未赋值到,(c) 引用是简单名称(没有像这样的限定符),并且 (d) 未从内部类中访问,则必须在引用该字段之前声明该字段。因此,满足所有四个条件的前向引用是非法的,但是在至少一个条件下失败的前向引用是可以的。this.
int a = a = 1;
编译,因为它违反了 (b):引用被赋值给,因此在 的完整声明之前引用是合法的。a
a
a
int b = this.b + 1
也编译,因为它违反了 (c):引用不是一个简单的名称(它用 ) 限定。这个奇怪的构造仍然是完全明确的,因为其值为零。this.b
this.
this.b
因此,基本上,初始值设定项中对字段引用的限制会阻止成功编译。int a = a + 1
请注意,字段声明将无法编译,因为 final 仍然是非法的前向引用。int b = (b = 1) + b
b
局部变量声明受 JLS §14.4 局部变量声明语句的约束。
局部变量的作用域在 JLS §6.3 声明的作用域中定义:
请注意,初始值设定项在所声明的变量的范围内。那么为什么不编译呢?int d = d + 1;
原因是由于Java关于确定赋值的规则(JLS §16)。定赋值基本上说,对局部变量的每次访问都必须有一个对该变量的先前赋值,Java编译器检查循环和分支以确保赋值总是在任何使用之前发生(这就是为什么定赋值有一个专用于它的整个规范部分)。基本规则是:
x
x
在 中,对 的访问解析为局部变量 fine,但由于在访问之前尚未分配,编译器会发出错误。在 中,首先发生,它赋值 ,然后初始化为该赋值的结果(即 1)。int d = d + 1;
d
d
d
int c = c = 1
c = 1
c
c
请注意,由于明确的赋值规则,局部变量声明将成功编译(与字段声明不同),因为在到达最终值时肯定会被赋值。int d = (d = 1) + d;
int b = (b = 1) + b
d
d
int x = x = 1;
等效于
int x = 1;
x = x; //warning here
而在
int x = x + 1;
首先,我们需要计算,但x的值是未知的,所以你得到一个错误(编译器知道x的值是未知的)x+1