class Test{
int p = (p=1) + p; // ERR "Cannot reference a field before it is defined"
int q = (q=1) + this.q; //fine!
void f() {
int t = (t=1) + t; // fine!
}
}
在第一种情况下,我了解:执行赋值(或后续添加?)时,p被视为未声明。
但是为什么在一个方法中它有所不同? OK t不会被视为未初始化,因为(t = 1)在加法之前执行。好的,t不是一个字段,但目前还没有声明!
我能以某种方式理解它吗?还是我只记住这种区别?
也许这也与以下内容有关:
static int x = Test.x + (x=1) + Test.x; // produces 2
void f() {
int y = y + (y=1) + y; // ERR local variable y may not have been initialized
}
为什么2?首先以某种方式评估(x = 1)(未声明x!),然后返回1,现在x已分配(!?)并包含1,因此两个Test.x均为1,但是(x = 1 )运算符还返回了1,因此求值Test.x + (x=1) + Test.x
表达式的结果是x应当为1 +1 + 1,并且3应该(重新分配)为x。
部分答案:实际上,结果是特定于实现的。 JLS仅保证评估二进制运算符的操作数的顺序(从左到右)。但是,如果我们有具有相同优先级的二元运算符(例如,加号),则不能保证它们的求值顺序。 在我的情况下,加上运算符首先在最左边计算,这就是为什么静态“ int x = Test.x(零)+(x = 1)+ Test.x(在(x = 1)之后为IS 1);”是0 + 1 + 1(请记住,x = 1是返回分配值的运算符)。 同样在我的情况下,方法是“ int y = y +(y = 1)+ y;”首先评估最左边的plus运算符(给出错误),但是如果JVM选择首先评估第二个plus运算符,那么可以保证首先评估其左操作数,并且(y = 1)将初始化y变量(因此代码会编译!)
我仍然不确定为什么(x = 1)不会被视为未通过字段声明。我隐约记得 JLS允许在LHS中使用未声明的变量(因此任何赋值都可行),但在RHS中则不允许(x ++,int sth = x)。我可以使用以下代码段将其记住:
class Test {
{ x = 7; } // fine! Initializer is like a regular method
int x;
static { y = 7; } // fine! Initializer is like a regular method
static int y;
P.S。这肯定不是Default Values and Initialization in Java的重复-那里没有直接的解释。在这里,我们不仅需要默认值(对于int为零)规则,而且还需要非常复杂的组合中的许多不同规则(运算符优先级,尤其是赋值的某些罕见特性!)。我也知道分配优先级最低,分配是运算符,它返回值!
答案 0 :(得分:1)
阅读Java Language Specification中局部变量声明的范围。例6.3-2中描述了您的确切问题。描述是这样的:
以下程序会导致编译时错误,因为局部变量p
的初始化在局部变量p
声明的范围之内,但是局部变量p
尚无值,无法使用。
答案 1 :(得分:0)
也许我不会非常详细,但是我会尝试一下,您指出了Java中可变生命周期的非常好的例子。
int p = (p=1) + p; // ERR "Cannot reference a field before it is defined"
在这种情况下,p
是一个类字段,当编译器加载一个类p时尚未初始化(第一次扫描类,因此p尚未加载到内存中并且无法求值)。
void f() {
int t = (t=1) + t; // fine!
}
在这种情况下,无论内部是什么,编译器都只会加载函数的定义(我的意思是如果没有语法错误,并且没有每个IDE都可以检查的错误类型)。这可能是一个奇怪的声明,但没关系,直到调用该函数并初始化t内联后才对其求值。
static int x = Test.x + (x=1) + Test.x; // produces 2
在这种情况下,x
是一个静态变量,在类之前加载了静态的“东西”,因此您可以想象编译器将x
字段放在您编写的所有内容之上。在这一行中,您说x等于1,所以1 +1 =2。这就像在做类似的事情
static int x = 1;
x = Test.x + Test.x;