为什么变量初始化为赋值表达式编译?

这怎么编译没有错误? 根据我的理解,编译器检查变量的类型(在本例中为String ),然后查看右侧表达式的类型是否对应于变量的类型(或者至少是一个子类型,但让我们坚持使用简单的情况String类,因为它是最终的)。

 public class InitClass { public static void main(String[] args) { String str = (str = "hello"); System.out.println(str); } } 

我的问题是str = "hello"如何编译? 编译器是否已经知道str应该是String类型的?

评估赋值表达式时

首先,评估左侧操作数以产生变量。 如果此评估突然完成,则赋值表达式出于同样的原因突然完成; 不评估右侧操作数,也不进行赋值。

这产生了变量str 。 然后

否则, 将评估右侧操作数 。 如果此评估突然完成,则赋值表达式会出于同样的原因突然完成,并且不会发生任何分配。

在您的示例中,右侧操作数本身是另一个赋值表达式。 因此str ,赋值运算符的右手操作数再次被计算以产生变量str 。 然后

否则,将右侧操作数的值转换为左侧变量的类型,进行值集转换(第5.113节)到相应的标准值集(不是扩展指数值集), 并且转换的结果存储在变量中。

所以"hello"存储在str 。 从那以后

在运行时,赋值表达式的结果是赋值发生后变量的值。 赋值表达式的结果本身不是变量。

"hello"赋值给str是值"hello" ,该值再次存储在str

你的情况相当于

 String str; str = (str = "hello"); 

虽然作业看起来很滑稽,但概念上没有任何错误。

然而,引用自身的变量初始化显然不是一个好主意。 编译器将尝试在很可能是程序错误的情况下标记它; 编译器有时无法这样做; 并且可能也会在其他时间过火。

局部变量具有更严格的要求(比字段变量更严格) – 必须在使用其值之前先分配它。 例如,这不会编译,因为本地var在分配之前被读取。

 String str; // local variable str = str; // error, try to read `str` before it's assigned 

字段变量始终具有默认初始值; 然而,编译器会检查明显的程序错误

 int x = x+1; // error. x is field variable. 

但是如果这样的检查失败则不是灾难性的,因为x在显式赋值之前具有值0

 int x; { x=x+1; } // ok. x==0, then x==1 after assignment 

但是,如果xfinal ,则上面的代码失败,因为编译器在读取x之前需要先明确赋值x ,对局部变量的要求相同。 但是这种检查可以被规避,因为不可能完全分析和防止它对于场变量

 final int x = (this).x+1; // compiles! 

在某些情况下,编译器过分,防止涉及lambda的合法用例

 Runnable r1 = ()->System.out.println(r1); // r1 is a field variable 

在这个用例中没有任何概念上的问题; 它可以被(this).规避(this). 太。

首先发生的是编译器识别引用的类型,然后知道这是一个字符串赋值给str的“hello”是有效的。