Why must local variables, including primitives, always be initialized in Java?
首先, 无论堆内还是栈中, 一块内存用完(即堆内成员不再被引用, 栈内方法出栈)后, 并不是说这块内容被清理干净(比如每一位都变为 0
), 它的内容并没有变化, 只是被标记为可用状态.
那么, 如果声明一个成员变量或局部变量, 没有初始化(或赋值), 也没有默认值(成员变量有默认值, 这里假设没有), 就可能出现读取到尚未擦除的敏感数据, 带来安全问题. 所以, 解决这个问题就有两种方法:
- 在声明时就给一个默认值, 这样就像成员变量一样;
- 在你读取内容之前, 就发现你没赋值, 提醒你有错, 强制你赋值, 就像局部变量一样.
那么为什么两个不一样呢? 我认为, 局部变量声明在方法中, 在方法内部, 局部变量的赋值和取值(读取)的顺序是确定的, 编译器是可以检查到读取局部变量之前你有没有给它赋值, 那么既然在编译器这里就能捕获错误, 那何乐而不为呢? 也就是说, 局部变量是可以具有默认值, 但如果编译器可以证明你正在尝试读取未初始化的变量, 就提前纠正你了, 那不更好吗? 所以与其为局部变量提供默认值, 更好的做法是强制你始终为其显式赋值。
但对于成员变量和静态变量来说, 编译器是无法知道调用方法的顺序的. 比如:
public class Test {
public String name;
public void dumpField() {
System.out.println("dumpField name=" + name);
}
}
name
的赋值可以发生在 dumpField()
之前, 也可以发生在 dumpField()
之后. 这是在运行时发生的, 在编译器来看确定不了的. 也就是说一个属性的 setter
和 getter
谁前谁后是不确定的, javac 也不知道, 所以就没办法提醒你是否可能产生错误和危险. 因此, java 就给了成员变量默认值(0
, false
, null
等), 也就是说成员变量至少有一个已知值, 保证就算取值在赋值之前也能读取一个安全值, 这也就消除了读取尚未擦除的敏感数据的潜在安全问题.
References:
[1] Why must local variables, including primitives, always be initialized in Java? - Stack Overflow