Java中未初始化的变量和成员

时间:2008-11-06 14:14:09

标签: java oop variables

考虑一下:

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”相同的运行时错误,这是一个编译时错误。为什么局部变量和成员之间存在差异?

编辑:让会员私密是我最初的意图,问题仍然存在......

7 个答案:

答案 0 :(得分:44)

语言以这种方式定义。

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

请参阅此处的4.5.5节 http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

答案 1 :(得分:22)

这是交易。当你打电话

TestClass tc = new TestClass();

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

  1. 在堆上为新对象分配内存。
  2. 将类字段设置为默认值(数字为0,布尔值为false,对象为null)。
  3. 调用构造函数(可能会重新启动字段,也可能不会。)
  4. 返回对新对象的引用。
  5. 因此,您的字段'a'和'b'都会启动到null,并且'a'会在构造函数中重新启动。此过程与方法调用无关,因此局部变量“c”从不初始化。

    HTH

    PS:对于严重失眠的人,请阅读this

答案 2 :(得分:13)

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

答案 3 :(得分:3)

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

答案 4 :(得分:3)

编译器可以从代码中告诉doSomething()c在那里声明并且从未初始化。因为它是本地的,所以不可能在其他地方初始化。

无法告诉您何时何地打电话给doSomething()。 b是公共会员。在调用方法之前,完全有可能在其他代码中初始化它。

答案 5 :(得分:2)

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

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

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

注意:null与undefined不同。

答案 6 :(得分:1)

你实际上已经确定了Java系统中一个较大的漏洞,通常是在编辑/编译时而不是运行时发现错误,因为 - 正如接受的答案所说 - 很难判断b是初始化还是不

有一些模式可以解决这个缺陷。首先是“默认为最终”。如果你的成员是最终的,你将不得不用构造函数填充它们 - 并且它将使用路径分析来确保每个可能的路径填充决赛(你仍然可以指定它“Null”,这会破坏目的但是至少你会被迫承认你是故意这样做的。)

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

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

还有一个“NotNullByDefault”包级别注释,它将使编辑器认为除非将其标记为Nullable,否则任何变量都不能具有空值。

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