tl; dr
对于字段,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
在类类型C(第8.1.6节)中声明或继承的成员的声明范围是C的整个主体,包括任何嵌套的类型声明。
- 实例变量的初始化表达式可以使用在类中声明或由类继承的任何静态变量的简单名称,即使是声明在文本后出现的静态变量也是如此。
- 有时会限制使用声明之后在文本中出现的实例变量,即使这些实例变量在范围内。有关控制对实例变量的前向引用的精确规则,请参见第8.3.2.3节。
§8.3.2.3说:
仅当成员的声明是类或接口C的实例(分别为静态)字段且满足以下所有条件时,才需要在成员声明之前以文本形式出现:
- 使用情况发生在C的实例(分别为静态)变量初始化程序或C的实例(分别为静态)变量初始化程序中。
- 用法不在作业的左侧。
- 用法是通过一个简单的名称。
- C是包含用法的最里面的类或接口。
实际上,在某些情况下,您可以在声明字段之前先对其进行引用。这些限制旨在防止类似
int j = i;
int i = j;
从编译。Java规范说:“以上限制旨在在编译时捕获循环或其他形式的初始化。”
这些规则实际上可以归结为什么?
简而言之,规则基本上说,如果(a)引用位于初始化程序中,(b)引用未分配给该字段,(c)引用是a ,则必须在对该字段的引用之前声明一个字段简单名称(没有限定词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编译器会检查循环和分支,以确保赋值始终在任何使用之前发生(这就是为什么明确赋值有专门的整个规范部分的原因对此)。基本规则是:
- 对于每次访问局部变量或空白的final字段
x
,x
必须在访问之前明确分配,否则会发生编译时错误。
在中int d = d + 1;
,对的访问d
被解析为局部变量fine,但是由于d
之前未分配访问权限d
,因此编译器会发出错误。在中int c = c = 1
,c = 1
首先发生,它分配c
,然后c
初始化为该分配的结果(为1)。
请注意,由于有明确的赋值规则,局部变量声明int d = (d = 1) + d;
将成功编译(与 field声明不同int b = (b = 1) + b
),因为d
在d
到达最终变量时已明确赋值。
static
class-scope变量中(如中)static int x = x + 1;
,是否会遇到相同的错误?因为在C#中,它是静态的还是非静态的都会有所不同。