终于有好奇心的Java未初始化的变量
-
12-09-2019 - |
题
我试图拿出晦涩测试用例一种替代开源JVM我正在与帮助(禽流一>)当我碰到的代码有趣的一点,我很惊讶,它没有编译:
public class Test {
public static int test1() {
int a;
try {
a = 1;
return a; // this is fine
} finally {
return a; // uninitialized value error here
}
}
public static void main(String[] args) {
int a = test1();
}
}
最明显的代码路径(唯一的一个,我看到)是执行= 1,“尝试”,返回一个(第一次),然后执行最后,这实际上返回一种。然而,javac的抱怨,“A”可能不会被初始化:
Test.java:8: variable a might not have been initialized return a; ^
我能想到的唯一的事情可能会引起/允许不同的代码路径是,如果一个不起眼的运行时异常是在尝试后开始出现,但之前的值1被分配给一个 - 这类似于一个OutOfMemoryError或一个StackOverflowException,但我不能认为其中这些可能可能发生在这个地方代码中的任何情况。
人比较熟悉的Java标准的细节可以提供一些线索这光?这只是其中的编译器是保守的情况下 - 因此拒绝编译否则这将是有效的代码 - 或者是一些陌生人回事
解决方案
这可能似乎直觉到异常可以在一个= 1行发生,但可能发生JVM错误。因此,留下变量a初始化。因此,编译器错误使得完整意义上的。这是的你提到的晦涩的运行时错误。但是,我认为,一个OutOfMemoryError是远离晦涩的,应由开发商至少想过。此外,记住,树立的OutOfMemoryError在另一个线程和推堆内存使用过去的限制量的一个动作可能发生的状态是变量a的分配。
不管怎么说,因为你在看编译器的设计,我也假设你已经知道它是多么的愚蠢到在finally块返回值。
其他提示
Java语言规范要求在使用前的变量被分配。该JLS
定义为被称为“定分配”规则的具体规则。所有的Java编译器需要坚持给他们。
V是最后块当且仅当V是try语句之前明确赋值之前明确赋值。
在另一个话,考虑到最后语句时,一个try-catch-finally
语句分配内的try和catch语句块不考虑。
不用说,该规范是非常保守的在这里,但他们宁愿有规范是简单而位有限(认为规则已经复杂)比手下留情,但很难理解和推理。
编译器必须遵循以下明确赋值规则,让所有的编译器发出同样的错误。编译器不允许比JLS
指定以抑制任何错误时执行任何额外的分析。
我相信这只是由于一个try-catch-终于关系的语义。从 Java语言规范:
如果try块的执行 正常完成,那么最终 执行块...
如果try块的执行 完成突然因为一抛 一值V ...
如果try块的执行 突然完成任何其他 原因R,则最后块是 执行...
在最后一种情况似乎是最相关的。看来,finally块应该能正确执行,如果try块以任何理由突然结束。显然,如果try块分配结束前finally块将是无效的。不过,如你所说,这是不是特别容易。
这是非常有可能的javac需要,使毯假设异常可以在try块的任何地方出现,甚至在分配,以及因此最终可能会返回一个未初始化的变量。从理论上讲它可以做一个详细的分析,并发现,在所有的路径通过try块“一个”总是被成功初始化,但是这是一个大量的工作,几乎没有收益。
现在,如果有人正好可以指出在Java语言规范中的相关章节...
编译器错误,以下(后续) 语句将运行状
int i=5;int d;
if(i<10)
{system.out.println(d);}
如果所述条件语句是肯定的和不可疑代码不会像
达到不会发生编译器错误int i;
if(true){}
else
{System.out.println(d);}
和如果所述条件语句肯定会发生,并且不可疑代码将像
达到将发生编译器错误int i;
if(true)
{System.out.println(d);}
else{}
作为try块下此来它们遵循相同的规则。
我想Java编译器假设最坏的情况 - 没有保证在try块东西,甚至是由于某种原因执行。因此,它的抱怨是有效的。变量可能没有被初始化。
编译器只是被保守这里。