考虑一下:
public class TestClass {
private String a;
private String b;
public TestClass()
{
a = "initialized";
}
public void doSomething()
{
String c;
a.notify(); // This is fine
b.notify(); // This is fine - but will end in an exception
c.notify(); // "Local variable c may not have been initialised"
}
}
我不明白。 “b”永远不会被初始化,但会产生与“c”相同的运行时错误,这是一个编译时错误。为什么局部变量和成员之间存在差异?
编辑:让会员私密是我最初的意图,问题仍然存在......
答案 0 :(得分:44)
语言以这种方式定义。
对象类型的实例变量默认为初始化为null。 默认情况下,对象类型的局部变量未初始化,访问未定义的变量是编译时错误。
请参阅此处的4.5.5节 http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
答案 1 :(得分:22)
这是交易。当你打电话
TestClass tc = new TestClass();
new
命令执行四项重要任务:
false
,对象为null
)。因此,您的字段'a'和'b'都会启动到null
,并且'a'会在构造函数中重新启动。此过程与方法调用无关,因此局部变量“c”从不初始化。
HTH
PS:对于严重失眠的人,请阅读this。
答案 2 :(得分:13)
明确分配的规则非常困难(阅读JLS第3版第16章)。在字段上强制执行明确的赋值是不切实际的。就目前而言,甚至可以在初始化之前观察最终字段。
答案 3 :(得分:3)
编译器可以确定永远不会设置c。在调用构造函数之后,但在doSomething()之前,b变量可以由其他人设置。将b设为私有,编译器可以提供帮助。
答案 4 :(得分:3)
编译器可以从代码中告诉doSomething()c在那里声明并且从未初始化。因为它是本地的,所以不可能在其他地方初始化。
无法告诉您何时何地打电话给doSomething()。 b是公共会员。在调用方法之前,完全有可能在其他代码中初始化它。
答案 5 :(得分:2)
成员变量初始化为null或默认原始值(如果它们是基元)。
局部变量是UNDEFINED并且未初始化,您负责设置初始值。编译器阻止您使用它们。
因此,当实例化类TestClass而未定义c时,b被初始化。
注意:null与undefined不同。
答案 6 :(得分:1)
你实际上已经确定了Java系统中一个较大的漏洞,通常是在编辑/编译时而不是运行时发现错误,因为 - 正如接受的答案所说 - 很难判断b是初始化还是不
有一些模式可以解决这个缺陷。首先是“默认为最终”。如果你的成员是最终的,你将不得不用构造函数填充它们 - 并且它将使用路径分析来确保每个可能的路径填充决赛(你仍然可以指定它“Null”,这会破坏目的但是至少你会被迫承认你是故意这样做的。)
第二种方法是严格的空检查。您可以通过项目或默认属性在eclipse设置中打开它。我相信它会强制你在调用它之前对你的b.notify()进行空值检查。这可能会很快失控,因此它倾向于使用一组注释来简化操作:
注释可能有不同的名称,但在概念中,一旦打开严格的空检查和注释,变量的类型就是“可空”和“非空”。如果您尝试将Nullable放入非null变量,则必须先将其检查为null。参数和返回类型也会被注释,因此您不必在每次分配给非空变量时都检查null。
还有一个“NotNullByDefault”包级别注释,它将使编辑器认为除非将其标记为Nullable,否则任何变量都不能具有空值。
这些注释主要适用于编辑器级别 - 您可以在eclipse和其他编辑器中打开它们 - 这就是为什么它们不一定是标准化的。 (至少我上次检查时,Java 8可能有一些我还没有找到的注释)