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

时间:2012-11-05 15:49:05

标签: java initialization conditional final

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。

的范围内

3 个答案:

答案 0 :(得分:46)

作为针对可移植性的一部分,有一组非常具体的规则,用于编译器应该接受什么以及它应该拒绝什么。这些规则在确定变量是否在其使用时明确赋值时,允许并且仅需要有限形式的流量分析。

请参阅Java语言规范Chapter 16. Definite Assignment

关键规则是16.2.7. if Statements中的一个,“if(e)S”情况。明确分配的规则扩展为:

V if(e)S 之后分配,并且仅当 S 之后分配了 V 时>和 V e 之后分配为false。

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

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

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

最终变量很好。规则实际上是: -

  

“如果将最终变量分配给除非,则为编译时错误   紧接着它之前肯定是未分配的(§16)   分配“。

声明使它绝对未分配,即使是有限的流量分析也可以确定x在赋值时仍然是未分配的。

答案 1 :(得分:26)

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

中定义
  

当对其值进行任何访问时,每个局部变量和每个空白最终字段必须具有明确赋值。

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

JLS #15.28定义常量表达式

  

编译时常量表达式是表示基本类型值的表达式或不突然完成的字符串,仅使用以下内容组成:

     
      
  • [...]
  •   
  • 引用常量变量的简单名称(第6.5.6.1节)(§4.12.4)。
  •   

JLS #4.12.4常量变量定义为:

  

基本类型或类型String的变量,是最终的并使用编译时常量表达式初始化,称为常量变量。

在您的情况下,final int x = 101;是常量变量,但final int x; x = 101;不是。

答案 2 :(得分:11)

您在第二个代码中为变量x做了什么,称为空白最终变量。如果最终变量在声明时未初始化,则称为空白最终变量。

许多Java开发人员认为最终变量的值在编译时是已知的。这并非总是如此。据说在编译时未知的空白最终变量的值。因此,您的第二个代码将给您一个编译错误。编译器可以看到你已经初始化了最终变量x,但是编译不知道它的值。所以编译器无法解析if语句。因此,它认为变量y未初始化。

您可以阅读有关Java最终变量here的更多信息。