为什么我们不能使用未初始化的局部变量从该变量的类型访问静态内容?

Psh*_*emo 76 java static initialization local-variables jls

尝试x通过未初始化的局部变量访问静态字段时,出现Foo foo; foo.x编译错误Variable 'foo' might not have been initialized

class Foo{
    public static int x = 1;

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

它可能看起来像这样的错误是有道理的,但直到我们意识到,访问static成员编译器不实际使用的变量,但只有它的类型

例如,我可以foo使用value 进行初始化,null这将使我们能够x毫无问题地进行访问:

Foo foo = null;
System.out.println(foo.x); //compiles and while running prints 1!!! 
Run Code Online (Sandbox Code Playgroud)

之所以如此,x是因为编译器意识到这是静态的,并foo.x像对待其编写时一样对待Foo.x(至少这是我到目前为止所认为的)。

那么,为什么编译器突然坚持要有foo一个在那个地方不会使用的值?

(免责声明:这不是在实际应用中使用的代码,只是一种有趣的现象,我在Stack Overflow上找不到答案,因此我决定对此进行询问。)

Nex*_*vis 72

§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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到dummyFoo()仍会进行评估,因为print会延迟5秒钟,Thread.sleep()即使它始终返回一个null被丢弃的值。

如果表达式未评价的print会瞬间出现,其可以当类中可以看出Foo,直接用于访问xFoo.x

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

  • 有趣的是,`javac`确实执行了此操作,生成了一条加载和弹出指令,而`ecj`则执行了正式规则,即,不允许通过未初始化的变量进行访问,但未生成无副作用的代码。 (4认同)

And*_*lko 19

第16章明确分配

在访问任何值时,每个局部变量(第14.4节)和最后一个空白字段(第4.12.4节,第8.3.1.2节)都必须具有一个明确分配的值。

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

要评估字段访问表达式 foo.x,必须首先评估其primary一部分(foo)。这意味着foo将发生对的访问,这将导致编译时错误。

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


rac*_*man 14

保持规则尽可能简单有其价值,“不要使用可能尚未初始化的变量”非常简单。

更重要的是,有一种建立静态方法的方法-始终使用类名,而不是变量。

System.out.println(Foo.x);
Run Code Online (Sandbox Code Playgroud)

变量“ foo”是不必要的开销,应删除,并且编译器错误和警告可以被视为有助于实现这一目标。