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的范围内。
您对第二个代码中的变量x
所做的操作被称为blank final variable。如果最终变量在声明时未初始化,则称为空白最终变量。
许多Java开发人员认为,最终变量的值在编译时已知。这并不总是正确的。据说,在编译时未知的空白最终变量的值。因此,您的第二个代码将给您一个编译错误。编译器可以看到您已经初始化了最后一个变量x
,但compile不知道它的值。所以编译器无法解析if语句。因此,它认为变量y
未初始化。
您可以在这里阅读更多关于Java最终变量的信息。
它与编译器如何确定语句是否将被执行有关。JLS#16中对其进行了定义:
当访问其值时,每个局部变量和每个空白的最终字段必须有一个明确的赋值。
在您的情况下,编译器无法确定y
已被明确分配,并给您一个错误。这是因为它需要确定条件始终为真,并且只有当if
中的条件是一个常量表达式时,才可能这样做。
JLS#15.28定义了常量表达式:
编译时常量表达式是一个表示原语类型的值或字符串的表达式,它不会突然完成,并且只使用以下内容组成:
JLS#4.12.4将常量变量定义为:
原语类型或字符串类型的变量称为常量变量,它是final,并用编译时常量表达式初始化。
在您的示例中,最终int x=101;
是一个常量变量,但最终int x; x=101;
不是。
作为可移植性目标的一部分,编译器应该接受什么和拒绝什么有一套非常具体的规则。当确定变量是否在使用时被明确分配时,这些规则允许也只需要有限形式的流分析。
参见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的设计者是否有任何理由认为不应为局部变量提供默认值?认真地讲,如果实例变量可以被赋予默认值,那为什么我们不能对局部变量做同样的事情呢? 问题答案: 声明局部变量主要是为了进行一些计算。因此,程序员决定设置变量的值,并且不应采用默认值。如果程序员错误地没有初始化局部变量并且使用默认值,则输出可能是一些意外值。因此,在使用局部变量的情况下,编译器将要求程序员在访问变量之前使用一些值
今天我遇到了一个有趣的问题,是我自己打错的。我创建了一个lambda,它接受了对结构的引用,并错误地将其设置为按值接收参数的std::函数。 这里有一个更简洁的版本: 使用godbolt检查显示,使用MSVC编译成功,但对于Clang和GCC都失败了。 这是MSVC编译器中的bug吗?
问题内容: 如果你给 它没有编译,但是带有花括号的相同代码是: 有什么解释? 问题答案: 基本上,变量声明只能在块中声明。 查看 Java语言规范中“语句”的语法 -它包括Block,但不包括LocalVariableDeclarationStatement- 后者是block语法的一部分。 这实际上是实用主义的问题:如果没有括号,则只能使用一个语句。如果没有后续语句,则声明变量是没有意义的,因为
变量被编译器声明为const,导致无法编译