Java不允许您使用可能尚未在方法范围内初始化的变量。类作用域内未初始化的变量仍可能由类方法返回,并且值默认为null。
为什么两种不同的处理范围不同?
public class TestClass {
Integer i;
Double d;
public TestClass() {
d = 1d;
}
public Double getD() {
return d;
}
public Integer getI() {
return i;
}
// public Integer getSomeInt() {
// Integer i;
// return i;
// }
public static void main(String[] args) {
TestClass myClass = new TestClass();
System.out.println(myClass.getI().getClass());
}
}
这会导致NullPointerException,但在getSomeInt()中返回i
是编译器错误,因为"变量可能尚未初始化"。
该规则的结果是,堆分配的所有内容都有一个默认值零(false、null,无论二进制零对类型的意义如何)。
因为成员变量有默认值(如果未初始化),所以I
有null
,如果在null上调用方法,则会导致NullPointerException
对于局部变量,在使用之前必须对其进行初始化,否则它将变成编译时错误
局部变量略有不同;编译器从不为未初始化的局部变量分配默认值。如果无法在声明的地方初始化本地变量,请确保在尝试使用它之前为其赋值。访问未初始化的本地变量将导致编译时错误。[….]
这真的很简单。成员变量会自动初始化为默认值,而局部变量则不会。
当你做
public Integer getSomeInt() {
Integer i;
return i;
}
您隐藏this.i
,而在return i
中,您引用了一个(未初始化的)本地变量。
那么为什么成员变量是自动初始化的,而局部变量不是?
最终,这是一个只有语言设计者才能回答的问题,但如果非要我猜测的话,我会说这是由于必须将所有分配的内存清零的性能问题。然而,当涉及到对象时,强迫程序员显式初始化所有字段将是一件痛苦的事情。
来自JLS(4.12.3种变量):
类变量是在准备好其类或接口时创建的(§12.3.2),并且初始化为默认值(§4.12.5)。
[…]
局部变量声明语句可能包含一个表达式初始化变量。带有初始化表达式的局部变量为但是,直到局部变量声明语句声明它已被执行。(确定性赋值规则(第16章,确定性赋值)阻止局部变量的值在已初始化或以其他方式分配了值。)
在调用super
之后,所有字段都在构造函数中隐式初始化。对象引用设置为null
,基元值设置为0
、false
等。这种隐式初始化不是在方法中完成的。