tl;博士
对于字段,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,声明的范围中定义。
相关规则有:
- 在类类型 C(第 8.1.6 节)中声明或继承的成员
m 的声明范围是整个 C 主体,包括任何嵌套类型声明。
- 实例变量的初始化表达式可以使用在类中声明或继承的任何静态变量的简单名称,即使是稍后在文本中声明的静态变量。
- 有时会限制使用在使用后以文本形式出现声明的实例变量,即使这些实例变量在范围内也是如此。有关管理对实例变量的前向引用的精确规则,请参见第 8.3.2.3 节。
§8.3.2.3 说:
成员的声明需要以文本形式出现在它之前
仅当成员是一个实例(分别为静态)字段时才使用
一个类或接口 C 并且满足以下所有条件:
- 在 C 的实例(分别为静态)变量初始化程序或 C 的实例(分别为静态)初始化程序中使用。
- 用法不在作业的左侧。
- 用法是通过一个简单的名称。
- C 是包含用法的最内层类或接口。
您实际上可以在声明字段之前引用它们,但在某些情况下除外。这些限制旨在防止类似
的代码
int j = i;
int i = j;
来自编译。 Java 规范说“上述限制旨在在编译时捕获循环或其他格式错误的初始化。”
这些规则实际上归结为什么?
简而言之,规则基本上说您必须在对该字段的引用之前声明一个字段,如果 (a) 引用在初始化程序中,(b)分配给,(c) 引用是一个简单名称(没有像this. 这样的限定符)和(d) 它不是从内部类中访问的。因此,满足所有四个条件的前向引用是非法的,但至少在一个条件上失败的前向引用是可以的。
int a = a = 1; 编译,因为它违反 (b):引用 a 被分配给,所以在 a 的完整声明之前引用 a 是合法的.
int b = this.b + 1 也可以编译,因为它违反了 (c):引用 this.b 不是一个简单的名称(它由 this. 限定)。这个奇怪的构造仍然是完美定义的,因为this.b 的值为零。
因此,基本上,初始化程序中对字段引用的限制会阻止 int a = a + 1 成功编译。
请注意,字段声明 int b = (b = 1) + b 将无法编译,因为最终的 b 仍然是非法的前向引用。
局部变量
局部变量声明由JLS §14.4,局部变量声明语句管理。
局部变量的作用域定义在JLS §6.3,声明的作用域:
- 块中局部变量声明的范围(第 14.4 节)是声明出现的块的其余部分,从它自己的初始化程序开始,并在局部变量声明语句的右侧包括任何进一步的声明符。
注意初始化器在被声明的变量的范围内。那么为什么int d = d + 1; 不编译呢?
原因是由于 Java 的明确分配规则 (JLS §16)。定义赋值基本上是说每次访问局部变量都必须先对该变量进行赋值,Java 编译器会检查循环和分支以确保赋值总是在任何使用之前发生(这就是为什么定义分配有一个专门的规范部分)。基本规则是:
- 对于局部变量或空白最终字段
x 的每次访问,必须在访问之前明确分配x,否则会发生编译时错误。
在int d = d + 1; 中,对d 的访问被解析为局部变量fine,但是由于在访问d 之前还没有分配d,所以编译器会报错。在int c = c = 1 中,首先发生c = 1,它分配c,然后c 被初始化为该分配的结果(即1)。
注意,由于明确的赋值规则,局部变量声明int d = (d = 1) + d;将编译成功(不像字段声明int b = (b = 1) + b),因为d是在达到最终的d 时确定分配。