54
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 语句的条件始终为真,因此 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 的范围内。

4

3 回答 3

46

作为实现可移植性的一部分,对于编译器应该接受什么和应该拒绝什么有一套非常具体的规则。在确定变量在使用时是否明确分配时,这些规则既允许也只需要有限形式的流分析。

请参阅 Java 语言规范第 16 章。定义赋值

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

Vif (e) S之后赋值 当且仅当V被赋值在S之后并且V被赋值在e之后当 false 时。

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

因此 y 不是在 if 语句之后明确分配的。

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

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

“如果最终变量被分配给它,这是一个编译时错误,除非它在分配之前绝对未分配(第 16 节)。”

该声明使其绝对未分配,即使是有限流分析也可以确定 x 在分配时仍然绝对未分配。

于 2012-11-05T15:56:18.207 回答
27

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

当发生对其值的任何访问时,每个局部变量和每个空白最终字段都必须具有明确分配的值。

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

JLS #15.28定义了常量表达式

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

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

JLS #4.12.4常量变量定义为:

原始类型或 String 类型的变量,它是 final 并使用编译时常量表达式进行初始化,称为常量变量。

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

于 2012-11-05T15:57:53.277 回答
11

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

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

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

于 2012-11-05T16:13:24.900 回答