考虑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 )不会发出任何编译时错误,即使它们没有显式初始化并且只需要初始化局部变量?
答案 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.