Java未初始化的变量,最终带来好奇心

Jos*_*ner 6 java variables initialization finally

当我遇到一些有趣的代码时,我试图为我正在帮助(Avian)的替代开源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();
    }
}
Run Code Online (Sandbox Code Playgroud)

最明显的代码路径(我看到的唯一一个)是执行a = 1,"尝试"返回(第一次),然后执行finally,实际返回a.但是,javac抱怨"a"可能尚未初始化:

    Test.java:8: variable a might not have been initialized  
        return a;  
               ^  

我能想到的唯一可能导致/允许不同代码路径的是,如果在try开始之后但在将值1分配给a之前发生了一个模糊的运行时异常 - 类似于OutOfMemoryError或StackOverflowException,但我无法想到这些可能发生在代码中的这个地方的任何情况.

任何更熟悉Java标准细节的人都可以对此有所了解吗?这只是编译器保守的情况 - 因此拒绝编译本来是有效代码的东西 - 或者这里有什么奇怪的事情?

Eli*_*jah 10

在a = 1行上可能发生异常似乎反直觉,但可能发生JVM错误.因此,将变量保持为未初始化.因此,编译器错误完全有意义.这是您提到的模糊运行时错误.但是,我认为OutOfMemoryError远非晦涩难懂,至少应该由开发人员考虑.此外,请记住,设置OutOfMemoryError的状态可能发生在另一个线程中,并且推动超过限制使用的堆内存量的一个操作是变量a的赋值.

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


not*_*oop 7

Java语言规范要求在使用变量之前分配变量.该JLS定义为被称为"定分配"规则的具体规则.所有Java编译器都需要遵守它们.

JLS 16.2.15:

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

换句话说,在考虑finally语句时,try-catch-finally不考虑语句赋值中的try和catch块语句.

毋庸置疑,这个规范在这里非常保守,但他们宁愿让规范变得简单而有点受限(相信规则已经很复杂),而不是宽容但难以理解和推理.

编译器必须遵循这些Definite Assignment规则,因此所有编译器都会发出相同的错误.不允许编译器执行除JLS指定之外的任何额外分析以抑制任何错误.