Java中未初始化的类成员不会发出任何编译器错误。然而局部变量呢。为什么?

时间:2011-12-15 14:49:59

标签: java

考虑Java中的以下代码片段。它不会编译。

package temppkg;

final public class Main
{
    private String x;
    private int y;

    private void show()
    {
        String z;
        int a;

        System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
        System.out.println(y); // Works fine displaying its default value which is zero.
        System.out.println(z.toString()); // Causes a compile-time error - variable z might not have been initialized.
        System.out.println(a); // Causes a compile-time error - variable a might not have been initialized.
    }

    public static void main(String []args)
    {
        new Main().show();
    } 
}

为什么在上面的代码片段中声明的类成员( x和y )不会发出任何编译时错误,即使它们没有显式初始化并且只需要初始化局部变量?

5 个答案:

答案 0 :(得分:22)

如有疑问,请检查Java Language Specification(JLS)。

the introduction中,您会找到:

  

第16章描述了语言确切的确切方式   在使用之前肯定设置了局部变量。所有其他   变量自动初始化为默认值Java   编程语言不会自动初始化局部变量   为了避免掩盖编程错误。

chapter 16州的第一段,

  

每个局部变量和每个空白的最终字段必须有一个明确的   当对其值的任何访问发生时赋值... .... Java编译器   必须进行特定的保守流量分析以确保   对于局部变量的每次访问或空白的最终字段f,f   在访问之前是明确分配的;否则编译时   必须发生错误。

默认值本身位于section 4.12.5。该部分打开:

  

每个类变量,实例变量或数组组件都是   在创建时使用默认值初始化。

...然后继续列出所有默认值。

JLS实际上并不难理解,我发现自己越来越多地使用它来理解为什么Java会做它的功能......毕竟,它是Java圣经!

答案 1 :(得分:3)

为什么他们会发出编译警告?,因为实例变量String将获得默认值null,int将获得默认值0。

编译器无法知道x.toString()会导致运行时异常,因为在运行之后实际上并未设置null的值。

答案 2 :(得分:1)

类成员可能已在代码中的其他位置初始化,因此编译器无法查看它们是否在编译时初始化。

答案 3 :(得分:1)

通常,编译器无法确定类成员之前是否已初始化。例如,您可以使用setter方法设置类成员的值,以及另一个访问该成员的方法。编译器在访问该变量时不能发出警告,因为它无法知道之前是否已调用过setter。

我同意在这种情况下(成员是私有的,并且没有单一的方法来编写变量),它似乎可能会引发编译器的警告。好吧,实际上你仍然不确定该变量是否已经初始化,因为它可以通过反射访问。

使用局部变量更容易,因为它们不能从方法外部访问,甚至也不能通过反射访问(afaik,如果错误请纠正我),因此编译器可能会更有帮助并警告我们未初始化的变量。

我希望这个答案可以帮助你:)

答案 4 :(得分:1)

构造(实例化)对象时,成员变量会自动初始化为其默认值。即使您手动初始化它们也是如此,它们将首先初始化为默认值,然后初始化为您提供的值。

这是一篇有点冗长的文章,但它解释了它:Object initialization in Java

然而,局部变量(在方法中声明的变量)不会自动初始化,这意味着您必须手动执行,即使您希望它们具有默认值。

您可以查看具有不同数据类型here的变量的默认值。

引用类型变量的默认值为null。这就是为什么它会在下面抛出NullPointerException

System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.

在下面的例子中,编译器足够聪明,知道变量尚未初始化(因为它是本地的,你还没有初始化它),这就是编译问题的原因:

System.out.println(z.toString()); // "Cuases a compile-time error - variable z might not have been initialized.