32

考虑一下:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = "initialized";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // "Local variable c may not have been initialised"
    }

}

我不明白。“b”从未初始化,但会给出与“c”相同的运行时错误,这是一个编译时错误。为什么局部变量和成员之间有区别?

编辑:将成员设为私有是我的初衷,但问题仍然存在......

4

7 回答 7

44

语言以这种方式定义它。

对象类型的实例变量默认初始化为 null。默认情况下不初始化对象类型的局部变量,访问未定义的变量是编译时错误。

请参阅 SE7 的第 4.12.5 节(与 SE14 相同的部分) http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

于 2008-11-06T14:23:40.027 回答
24

这是交易。你打电话时

TestClass tc = new TestClass();

new命令执行四项重要任务:

  1. 在堆上为新对象分配内存。
  2. 将类字段初始化为其默认值(数字为 0,布尔值为false,对象为null)。
  3. 调用构造函数(可能会重新启动字段,也可能不会)。
  4. 返回对新对象的引用。

因此,您的字段 'a' 和 'b' 都被初始化为null,并且 'a' 在构造函数中被重新初始化。此过程与方法调用无关,因此永远不会初始化局部变量“c”。

高温高压

PS:对于严重失眠的人,请阅读内容。

于 2008-11-06T14:41:21.253 回答
14

明确分配的规则非常困难(请阅读 JLS 第 3 版的第 16 章)。在字段上强制执行明确的分配是不切实际的。就目前而言,甚至可以在初始化之前观察最终字段。

于 2008-11-06T15:59:18.367 回答
3

编译器可以确定 c 永远不会被设置。b 变量可以由其他人在调用构造函数之后设置,但在 doSomething() 之前。将 b 设为私有,编译器可能会提供帮助。

于 2008-11-06T14:16:52.113 回答
3

编译器可以从 doSomething() 的代码中看出 c 在那里声明并且从未初始化。因为是本地的,所以不可能在别处初始化。

它无法告诉您何时何地调用 doSomething()。b 是公共成员。在调用该方法之前,您完全有可能在其他代码中对其进行初始化。

于 2008-11-06T14:18:19.053 回答
2

成员变量初始化为 null 或它们的默认原始值(如果它们是原始值)。

局部变量未定义且未初始化,您负责设置初始值。编译器会阻止您使用它们。

因此,当类 TestClass 被实例化而 c 未定义时,b 被初始化。

注意:null 与 undefined 不同。

于 2012-04-14T06:59:25.967 回答
1

您实际上已经确定了 Java 系统中较大的漏洞之一,即通常尝试在编辑/编译时而不是运行时查找错误,因为 - 正如公认的答案所说 - 很难判断 b 是否已初始化。

有几种模式可以解决这个缺陷。首先是“默认情况下最终”。如果您的成员是最终成员,则必须使用构造函数填充它们-并且它将使用路径分析来确保每个可能的路径都填充在决赛中(您仍然可以将其分配为“空”,这会破坏目的,但是至少你会被迫承认你是故意这样做的)。

第二种方法是严格的空值检查。您可以通过项目或默认属性在 Eclipse 设置中打开它。我相信它会迫使你在调用它之前对你的 b.notify() 进行空检查。这很快就会失控,因此它倾向于使用一组注释来使事情变得更简单:

注释可能有不同的名称,但在概念上,一旦您打开严格的空检查,并且注释的变量类型为“可空”和“非空”。如果您尝试将 Nullable 放入非空变量中,则必须首先检查它是否为空。参数和返回类型也进行了注释,因此您不必每次分配给非空变量时都检查空值。

还有一个“NotNullByDefault”包级别注释,它将使编辑器假定没有变量可以具有空值,除非您将其标记为 Nullable。

这些注释主要应用于编辑器级别——您可以在 eclipse 和可能的其他编辑器中打开它们——这就是为什么它们不一定是标准化的。(至少上次我检查时,Java 8 可能有一些我还没有找到的注释)

于 2017-01-26T19:21:20.293 回答