为什么Java编译器不理解这个variables总是被初始化?

class Foo{ public static void main(String args[]){ final int x=101; int y; if(x>100){ y=-1; } System.out.println(y); } } 

Java编译器理解if语句的条件总是为true,因此y将始终被初始化。 没有编译错误,正如所料。

 class Bar{ public static void main(String args[]){ final int x; x=101; int y; if(x>100){ y=-1; } System.out.println(y); } } 

但是当我把x的声明和初始化分解成两行时,编译器似乎并没有得到这个条件总是正确的,y总是被初始化。

 final int x; x=101; byte b; b=x; System.out.println(b); 

同样的事情发生在这里,编译器会导致精度错误的丢失。

 final int x=101; byte b; b=x; System.out.println(b); 

再次,编译器可以理解x在b的范围内。

它与编译器如何确定语句是否被执行有关。 它在JLS#16中定义:

每个局部variables和每个空白的最终字段都必须有一个明确赋值的值,当它的值发生任何访问时。

在你的情况下,编译器不能确定y已被明确分配,并给你一个错误。 这是因为它需要确定条件始终为真,并且只有if中的条件是常量expression式才有可能。

JLS#15.28定义了常量expression式

编译时常量expression式是一个expression式,表示一个原始types的值或一个不会突然完成的string,并且仅由以下内容组成:

  • […]
  • 简单名称(§6.5.6.1)引用常量variables(§4.12.4)。

JLS#4.12.4将常量variables定义为:

原始types或types为String的variables(最终的并使用编译时常量expression式进行初始化)称为常量variables。

在你的情况, final int x = 101; 是一个常量variables,但final int x; x = 101; final int x; x = 101; 不是。

作为可移植性目标的一部分,编译器应该接受什么以及应该拒绝什么,有一组非常具体的规则。 这些规则在确定一个variables是否在使用时被明确赋值时,只允许和只需要有限的stream量分析forms。

请参阅Java语言规范第16章定义分配

关键的规则是16.2.7中的那个。 如果陈述 ,“如果(e)S”的情况。 被明确分配的规则扩展到:

Vif(e)S之后赋值, 且仅当VS之后赋值, V赋值在e之后,否则为false。

y是相关的V。 在if语句之前它是未分配的。 它确实是在S ,y = {y = -1;}之后分配的,但当x> 100为假时没有任何东西被赋值。

因此,在if语句之后y并不是明确赋值的。

更完整的stream程分析将确定条件x> 100始终为真,但JLS要求编译器根据这些特定规则拒绝程序。

最后的variables是好的。 规则实际上是: –

“如果最后一个variables被分配给它,那么编译时错误,除非它在分配之前立即被分配(第16节)”。

声明将其明确地赋予未赋值,甚至有限的stream分析也可以确定x在赋值时仍然是未赋值的。

你在第二个代码中为variablesx做了什么叫空白最终variables。 如果最后一个variables在声明时没有被初始化,那么它就被称为空白的最终variables。

许多Java开发人员认为编译时已知最终variables的值。 这并非总是如此。 据说在编译时不知道空白最终variables的值。 因此,你的第二个代码会给你一个编译错误。 编译器可以看到你已经初始化了最后一个variablesx ,但编译并不知道它的值。 所以编译器无法parsingif语句。 因此它认为variablesy没有被初始化。

您可以在这里阅读更多关于Java最终variables。