Java未初始化变量,最终为

Java未初始化变量,最终为,java,variables,finally,initialization,Java,Variables,Finally,Initialization,当我遇到一段有趣的代码时,我正试图为我正在帮助()的另一种开源JVM提供一些模糊的测试用例,我很惊讶它没有编译: public class Test { public static int test1() { int a; try { a = 1; return a; // this is fine } finally { return a; // uninitiali

当我遇到一段有趣的代码时,我正试图为我正在帮助()的另一种开源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();
    }
}
最明显的代码路径(我看到的唯一一条)是执行a=1,“尝试”返回a(第一次),然后执行finally,它实际上返回a。但是,javac抱怨“a”可能尚未初始化:

Test.java:8: variable a might not have been initialized return a; ^ java:8:变量a可能尚未初始化 返回a; ^ 我能想到的唯一一件可能导致/允许不同代码路径的事情是,如果在尝试开始之后,但在值1被分配给-类似于OutOfMemoryError或StackOverflowException的东西之前,发生了一个模糊的运行时异常,但是我想不出在代码中的这个地方会发生什么情况


对Java标准的细节更熟悉的人能解释一下吗?这仅仅是一种编译器保守的情况——因此拒绝编译本应是有效代码的代码——还是这里发生了一些奇怪的事情?

编译器在这里只是保守而已。

我相信这只是由于try-catch-finally关系的语义造成的。从:

如果执行try块 正常完成,然后最终 块被执行

如果执行try块 由于投掷而突然完成 价值观

如果执行try块 为任何其他人突然完成 原因是R,那么最后一个块是 执行


最后一个案例似乎与此最相关。如果try块由于任何原因突然完成,那么finally块应该能够正确执行。显然,如果try块在赋值之前结束,finally块将无效。不过,正如你所说,这种可能性不大

很可能需要javac做出一个全面的假设,即try块中的任何一点都可能发生异常,即使在赋值过程中也是如此,因此finally可能返回一个未初始化的变量。理论上,它可以进行详细的分析,并发现在通过try块的所有路径中,“a”总是能够成功初始化,但这是一个几乎没有收益的大量工作


现在如果有人能指出Java语言规范中的相关部分

a=1行可能会出现异常,但可能会出现JVM错误,这似乎与直觉相反。因此,使变量a未初始化。因此,编译器错误是完全有意义的。这就是您提到的那个模糊的运行时错误。然而,我认为OutOfMemoryError一点也不模糊,至少开发人员应该考虑一下。此外,请记住,设置OutOfMemoryError的状态可能发生在另一个线程中,而使堆内存使用量超过限制的操作是变量a的赋值


无论如何,既然您正在研究编译器设计,我也假设您已经知道在finally块中返回值是多么愚蠢。

Java语言规范要求在使用变量之前分配变量。
JLS
定义了称为“确定分配”规则的特定规则。所有Java编译器都需要遵循它们

:

V在finally块之前被明确赋值,iff V在try语句之前被明确赋值

换句话说,在考虑finally语句时,不考虑
try-catch-finally
语句分配中的try-and-catch块语句

不用说,该规范在这里是非常保守的,但是他们宁愿规范简单而有点有限(相信规则已经很复杂),而不是宽大但难以理解和推理


编译器必须遵循这些明确的赋值规则,因此所有编译器都会发出相同的错误。编译器不允许执行任何额外的分析,除非JLS指定要抑制任何错误。

我猜Java编译器假设了最坏的情况-无法保证try块中的任何内容都会由于某种原因被执行。因此,它的投诉是有效的。变量可能尚未初始化。

如果编译器不确定以下情况(后续),则条件块中会出现编译器错误 语句的运行方式如下

int i=5;int d;
if(i<10)
{system.out.println(d);}
if(true){}

如果条件语句肯定会出现,并且毫无疑问的代码会像

int i;
int i;
if(true)
{System.out.println(d);}
else{}

当try块位于此项下时,它们遵循相同的规则。

此行为由语言规范指定。编译器必须发出该错误,并且不允许使用任何额外的分析来抑制该错误。@msaeed:谢谢,这正是我所怀疑的。实际上,编译器在这里必须是保守的;请看@msaed的答案。我尽了最大努力在Avian中练习JSR(跳转子程序)jitting代码(为此,我不得不使用ECJ,因为sun javac很少再生成JSR),而我浓缩这个示例的测试用例要复杂得多。我很清楚这样的代码是毫无意义的,但是如果它能够被编译,那么必须正确地运行它。从我为Sun SCJP学习到的到目前为止,编译器在条件代码块中分配变量时遇到了问题。代码在没有finally的情况下运行良好,因此它假定try块将发生。我相信,由于所有异常都发生在运行时,因此编译器不关心这些异常
int i;
if(true)
{System.out.println(d);}
else{}