Java中未初始化的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”相同的运行时错误,这是一个编译时错误。 为什么局部variables和成员之间的区别?

编辑 :使会员隐私是我的初衷,问题依然存在…

明确的分配规则是相当困难的(阅读JLS第三版第16章)。 在字段上执行明确的分配是不切实际的。 就目前而言,甚至有可能在初始化之前观察最终的领域。

语言以这种方式定义它。

对象types的实例variables默认被初始化为null。 对象types的局部variables在默认情况下不会被初始化,访问一个未定义的variables会导致编译时错误。

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

这是交易。 你打电话时

 TestClass tc = new TestClass(); 

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

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

所以你的字段'a'和'b'都被初始化为null ,并且在构造函数中重新启动'a'。 这个过程与方法调用无关,所以局部variables'c' 不会被初始化。

HTH

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

编译器可以知道c永远不会被设置。 bvariables可以在调用构造函数之后由别人设置,但是在doSomething()之前。 使b是私有的,编译器可能会有所帮助。

编译器可以从doSomething()的代码中知道c是在那里声明的,从来没有初始化过。 因为是本地的,所以不可能在其他地方初始化。

它不能告诉你什么时候或在哪里打电话doSomething()。 b是公众成员。 在调用方法之前,完全有可能在其他代码中初始化它。

如果成员variables是基元,则初始化成员variables为null或其默认基元值。

局部variables是UNDEFINED的,不会被初始化,你负责设置初始值。 编译器阻止你使用它们。

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

注意:null与undefined不同。

实际上,您已经确定了Java系统中的一个更大的漏洞,通常会在编辑/编译时尝试查找错误,而不是运行时间,因为正如接受的答案所述,很难判断b是否已初始化。

有几种模式可以解决这个缺陷。 首先是“默认最终”。 如果你的成员是最终的,你将不得不用填充他们的构造函数 – 它会使用path分析来确保每一个可能的path填补决赛(你仍然可以指定它“失败”,这将打败目的,但至less你会被迫认出你是故意的)。

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

注释可能有不同的名称,但在概念上,一旦你打开严格的空检查和注释variables的types是“可空”和“不空”。 如果尝试将Nullable放置到非空variables中,则必须首先检查它是否为null。 参数和返回types也被注解,所以你不必在每次分配给一个非空variables的时候检查null。

还有一个“NotNullByDefault”包级别注释,它会使编辑器假定没有variables可以有空值,除非你标记为可空。

这些注释主要在编辑器级别应用 – 您可以在eclipse内或其他编辑器中打开它们,这就是为什么它们不一定是标准化的。 (至less上次我查了一下,Java 8可能有一些注释我还没find)