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

问题描述 投票:76回答:4

请看下面的代码。

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
    }
}

当你试图访问静态字段时,你会发现 x 经由 未初始化 局部变量 Foo foo; 编码 foo.x 产生编译错误。Variable 'foo' might not have been initialized.

它可能是 似乎 这样的错误是有意义的,但只有在我们意识到要访问一个 static 成员,JVM实际上并不 使用价值 的,而只是它的 类型.

例如,我可以初始化 foo 有价值 null 这样我们就可以访问 x 没有任何问题。

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

这种情况下,编译器的工作原理是: x 是静态的,并将 foo.x 如是 Foo.x 至少我到现在为止是这么认为的)。

那么为什么编译器突然坚持要用 foo 有价值 它不会使用 完全没有?


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

java static initialization local-variables jls
4个回答
73
投票

§15.11. 字段访问表达式:

如果该领域是: 静态:

对初级表达式进行评估,结果被丢弃。. 如果Primary表达式的评估突然完成,字段访问表达式也会因为同样的原因突然完成。

前面说到字段访问是由 Primary.Identifier.

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

EDIT:

这里有一个简短的例子,只是为了直观地演示一下。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);
    }
}

这里你可以看到 dummyFoo() 仍在评估,因为 print 延迟了5秒 Thread.sleep() 尽管它总是返回一个 null 值,该值被丢弃。

如果表达式没有被评估,那么 print 会立即出现,这一点可以看出,当类 Foo 直接用于访问 xFoo.x.

注意: 方法调用也被认为是一种 Primary 如图 §15.8 主要表达方式.


20
投票

第十六章. 肯定性分配

每个局部变量(§14.4)和每个空白的最终字段(§4.12.4, §8.3.1.2) 当对其值进行访问时,必须有一个确定的赋值。

其实这并不重要 什么 你试图通过一个局部变量来访问。规则是,在这之前,它应该被肯定地分配。

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

对于每一个局部变量或空白最终字段x的访问,x必须在访问前被明确赋值。或发生编译时错误。


14
投票

尽可能简单的规则是有价值的,"不要使用一个可能还没有初始化的变量 "就是最简单的。

更重要的是,调用静态方法有一个既定的方法--总是使用类名,而不是变量。

System.out.println(Foo.x);

变量 "foo "是不必要的开销,应该被移除,而编译器的错误和警告可以看作是帮助导致了这一点。


3
投票

其他答案完美地解释了背后发生的机制。也许你还想知道Java规范背后的原理。我不是Java专家,不能给你原原本本的理由,但让我指出这一点。

  • 每一段代码要么是有意义的,要么是会引发编译错误。
  • (对于静态,因为一个实例是不必要的。Foo.x 是自然的)。)
  • 现在,我们该如何处理 foo.x (通过实例变量访问)?
    • 这可能是编译错误,就像在C#中一样,或者是
    • 它有一个意义。因为 Foo.x 已经是 "简单访问 "的意思 x",按理说 表情 foo.x 有着不同的意义;也就是说。言之有物 和访问 x.

希望有识之士能告诉我们真正的原因吧:-)

© www.soinside.com 2019 - 2024. All rights reserved.