为什么我们不能通过未初始化的局部变量访问静态内容?

看看下面的代码:

class Foo{
    public static int x = 1;
}

class Bar{    
    public static void main(String[] args) {
        Foo foo;
        System.out.println(foo.x); // Error: Variable 'foo' might not have been initialized
    }
}

正如您所看到的,当您尝试通过未初始化的局部变量代码访问静态字段时,会生成编译错误:。xFoo foo;foo.xVariable 'foo' might not have been initialized

这个错误似乎是有道理的,但直到我们意识到要访问一个成员,JVM实际上并没有使用变量的值,而只使用的类型static

例如,我可以用值初始化,这将使我们能够毫无问题地访问:foonullx

Foo foo = null;
System.out.println(foo.x); //compiles and at runtime prints 1!!! 

这样的场景之所以有效,是因为编译器意识到它是静态的,并且像这样对待它(至少到目前为止我是这样想的)。xfoo.xFoo.x

那么,为什么编译器突然坚持要有一个它根本不会使用的值呢?foo


免责声明:这不是在实际应用中使用的代码,而是我在Stack Overflow上找不到答案的有趣现象,所以我决定询问它。


答案 1

§15.11.字段访问表达式

如果字段是静态的:

计算主表达式,并丢弃结果。如果主表达式的计算突然完成,则字段访问表达式会出于同样的原因突然完成。

在前面的地方,它指出字段访问由 标识。Primary.Identifier

这表明,即使它似乎没有使用,它仍然被评估,然后结果被丢弃,这就是为什么它需要初始化。当评估停止引用中所述的访问时,这可能会有所不同。Primary

编辑:

下面是一个简短的示例,只是为了直观地演示即使结果被丢弃,也会评估 :Primary

class Foo {
    public static int x = 1;
    
    public static Foo dummyFoo() throws InterruptedException {
        Thread.sleep(5000);
        return null;
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println(dummyFoo().x);
        System.out.println(Foo.x);
    }
}

在这里,您可以看到它仍然被计算,因为它被延迟了5秒,即使它总是返回一个被丢弃的值。dummyFoo()printThread.sleep()null

如果未计算表达式,则 将立即显示,当类直接用于访问时可以看到。printFooxFoo.x

注意:方法调用也被视为 §15.8 主表达式中所示的方法调用。Primary


答案 2

第 16 章。明确赋值

每个局部变量 (§14.4) 和每个空白的最终字段 (§4.12.4, §8.3.1.2) 在访问其值时必须具有明确赋值的值。

您尝试通过局部变量访问的内容并不重要。规则是,在此之前应该明确分配它。

要计算字段访问表达式,必须首先计算其主要部分 ()。这意味着将发生访问,这将导致编译时错误。foo.xfoofoo

对于局部变量或空白最终字段 x 的每次访问,必须在访问之前明确分配 x,否则会发生编译时错误。