【问题标题】:Why is unitialized object in Java not set to null?为什么Java中未初始化的对象未设置为null?
【发布时间】:2011-12-22 09:16:40
【问题描述】:

我在这里遇到了一个非常基本的问题,即使它必须存在,我也无法找到解决方案。

我有以下代码:

public class Foo {
    public static void main(String[] args) {
        String foo;
        if (foo != null) {
            System.out.println("foo");
        } else {
            System.out.println("foo2");
        }
    }
}

它给了我

''变量可能没有被初始化''

。为什么我必须显式分配 null 而不是所有变量都默认为 null 初始化?

【问题讨论】:

  • 因为事情就是这样......
  • 只有类级别的成员变量,如果你没有显式地初始化它们,它们会被自动初始化;局部变量不会自动初始化。这些只是 Java 编程语言的规则。

标签: java null initialization


【解决方案1】:

它可以防止您在明确分配之前意外使用局部变量。这是一件的事情 - 它有助于防止错误。遗憾的是,它不能用于字段(实例和静态变量)——但除了最终变量之外,编译器不知道你什么时候要读取它们,什么时候要分配它们——它无法告诉您调用方法的顺序。在单个方法中,它包含更多关于流控制的信息,因此可以为您提供更多检查。

如果您希望将局部变量的值设为空,为什么不直接设置它为空?这不仅让编译器满意——它也让你的意图变得清晰。

有关明确分配的更多详细信息,请参阅chapter 16 of the Java Language Specification

这是一个如何防止错误的示例:

Iterable<String> collectionOfNames = ...; // Some method call
String lastNameInCollection;
for (String name : collectionOfNames)
{
    lastNameInCollection = name;
}
System.out.println("The last name was: " + lastNameInCollection);

如果集合为空怎么办? 也许我们想打印“The last name was: null”——但也许我们应该做点别的。上面无法编译的事实(因为lastNameIncollection 可能未分配)迫使程序员考虑名称集合为空的情况。

【讨论】:

  • 我认为这并不能回答问题。这不是编译器抱怨的原因,而是为什么局部变量不会像类的成员一样自动使用默认值初始化,以及为什么必须显式地这样做。
  • @Reboot:不,我已经解释了原因:这是为了避免你在它们被赋予价值之前不小心使用它们。
  • 如果局部变量被赋予一个默认值,那么问题就不会存在。这就是问题所在:为什么没有为局部变量分配默认值?所以解释为什么编译器会阻止你使用一个未初始化的值,并不能解释为什么你必须手动初始化变量。
  • @Reboot: 不,问题存在 - 你意思是只读取已分配特定值的局部变量的问题,但您最终会经历一个分配该值的执行路径。如果你想要一个初始值,你可以很容易地给出一个 - 但我很高兴编译器阻止我尝试读取一个没有明确被赋予值的局部变量。
  • @Reboot:我已经回答过好几次了:为了防止错误。如果您的代码旨在通过两条路线之一来分配值,但实际上还有第三条路线不会分配值,我为什么不想知道在编译时?该语言旨在避免您犯这种错误。我不知道我怎么能比这更清楚......
【解决方案2】:

保持变量未初始化有时是一件好事,让编译器在使用它之前检查它是否已在代码的所有可能分支中分配。例如,如果您有一个复杂的条件,它分配了一个变量并且永远不应该是null

String variable;
if (conditionA) {
    if (conditionB)
        variable = "B";
    else
        variable = "A";
} else {
    switch (conditionC) {
    case 1:
        variable = "C1";
        break;
    case 2:
        variable = "C2";
        break;
    default:
        variable = "CD";
        break;
    }
}
System.out.println(variable.length());

如果您忘记在其中一个分支中分配变量,编译器会报错。既然您知道,您永远不会分配 null,您可以安全地使用该变量,而无需检查它是否有 null。如果您在定义中使用null 初始化变量并且忘记将变量设置为一个值,编译器将无法检查这一点,您可以获得NullPointerException

String variable = null;
if (conditionA)
    variable = "A";
// NullPointerException if conditionA is false, not check by compiler
System.out.println(variable.length());

变量也可以是final的,在这种情况下它只能被赋值一次。如果您使用默认值对其进行初始化,则它已经被分配并且无法重新分配:

final int variable;
if (condition)
    variable = 1;
else
    variable = 2;

【讨论】:

    【解决方案3】:

    简单的答案是:
    成员局部变量不会使用“null”值自动初始化。 但是类成员变量是。

    此代码将起作用

    public class Foo {
      String foo;
      public static void main(String[] args) {
        if (foo != null) {
            System.out.println("foo");
        } else {
            System.out.println("foo2");
        }
      }
    }
    

    或者这个

    public class Foo {
      public static void main(String[] args) {
        String foo = null;
        if (foo != null) {
            System.out.println("foo");
        } else {
            System.out.println("foo2");
        }
      }
    }
    

    如果您想全面深入了解这个主题,请考虑 Jon Skeet 的回答并查看他提供的链接。

    【讨论】:

      【解决方案4】:

      也可以完全不需要初始化,如果以后肯定要初始化的话:

          String foo;
          if (specialUseCase) {
              foo = "this";
          } else {
              foo = "that";
          }
      

      【讨论】:

        【解决方案5】:

        由于局部变量不会自动初始化,所以必须显式初始化。即使没有显式初始化,也只有成员变量初始化为null或默认值。如果将 String foo; 保留在方法 main() 之外,则不需要初始化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多