当前位置: 首页 > 知识库问答 >
问题:

为什么Java编译器不明白这个变量总是初始化的?

殳宸
2023-03-14
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个答案

陆甫
2023-03-14

您对第二个代码中的变量x所做的操作被称为blank final variable。如果最终变量在声明时未初始化,则称为空白最终变量。

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

您可以在这里阅读更多关于Java最终变量的信息。

瞿兴朝
2023-03-14

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

当访问其值时,每个局部变量和每个空白的最终字段必须有一个明确的赋值。

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

JLS#15.28定义了常量表达式:

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

  • […]
  • 指常量变量的简单名称(§6.5.6.1)(§4.12.4)

JLS#4.12.4将常量变量定义为:

原语类型或字符串类型的变量称为常量变量,它是final,并用编译时常量表达式初始化。

在您的示例中,最终int x=101;是一个常量变量,但最终int x; x=101;不是。

萧安怡
2023-03-14

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

参见Java语言规范第16章。明确分配

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

V在if(e)S之后赋值,当且仅当V在S之后赋值,V在e之后赋值时为false。

y是相关的V。在if语句之前未赋值。它确实是在S之后赋值的,y={y=-1;}但是当x

因此,y不一定是在if语句之后赋值的。

更完整的流动分析将确定条件x

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

“如果最后一个变量被赋值,则是编译时错误,除非在赋值之前它肯定是未赋值的(§16)。”

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

 类似资料:
  • 问题内容: Java编译器了解if语句的条件始终为true,因此y将始终被初始化。没有编译错误,如预期的那样。 但是,当我将x的声明和初始化分为两行时,编译器似乎没有得到条件始终为true且y将始终被初始化的信息。 同样的事情在这里发生,编译器会损失精度误差。 同样,编译器可以理解x在b的范围内。 问题答案: 它与编译器如何确定是否执行语句有关。它在JLS#16中定义: 每个局部变量和每个空白的f

  • 我尝试了这些编译器: gcc 4.9.2 with-wall-wextra 启用所有警告的Microsoft Visual C++2013

  • 问题内容: 如果你给 它没有编译,但是带有花括号的相同代码是: 有什么解释? 问题答案: 基本上,变量声明只能在块中声明。 查看 Java语言规范中“语句”的语法 -它包括Block,但不包括LocalVariableDeclarationStatement- 后者是block语法的一部分。 这实际上是实用主义的问题:如果没有括号,则只能使用一个语句。如果没有后续语句,则声明变量是没有意义的,因为

  • 问题内容: Java的设计者是否有任何理由认为不应为局部变量提供默认值?认真地讲,如果实例变量可以被赋予默认值,那为什么我们不能对局部变量做同样的事情呢? 问题答案: 声明局部变量主要是为了进行一些计算。因此,程序员决定设置变量的值,并且不应采用默认值。如果程序员错误地没有初始化局部变量并且使用默认值,则输出可能是一些意外值。因此,在使用局部变量的情况下,编译器将要求程序员在访问变量之前使用一些值

  • 今天我遇到了一个有趣的问题,是我自己打错的。我创建了一个lambda,它接受了对结构的引用,并错误地将其设置为按值接收参数的std::函数。 这里有一个更简洁的版本: 使用godbolt检查显示,使用MSVC编译成功,但对于Clang和GCC都失败了。 这是MSVC编译器中的bug吗?

  • 变量被编译器声明为const,导致无法编译