试想一下:
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”,这是一个编译时错误。 为什么本地变量和成员之间的区别?
编辑 :使会员私人是我的初步意向,问题仍然有效...
对于明确赋值的规则是相当困难的(读JLS第3版的第16章)。 这是不实际执行上的字段明确赋值。 既然这样,它甚至有可能它们被初始化之前,观察最终的领域。
语言定义了这种方式。
到对象类型默认的实例变量初始化为null。 对象类型的局部变量默认情况下不进行初始化,这是一个编译时错误访问未定义的变量。
见4.5.5在这里http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5
这里的交易。 你打电话的时候
TestClass tc = new TestClass();
在new
命令执行了四个重要的任务:
- 分配在堆新对象存储。
- 启动类字段为默认值(数值运算到0,布尔值,
false
,对象null
)。 - 调用构造方法(可以重新启动域,也可能不会)。
- 返回新对象的引用。
所以,你的域“A”和“B”都开始向null
,而“A”是在构造函数重新启动。 这个过程是不相关的方法调用,所以局部变量的“C”是永远不会初始化。
HTH
PS:对于严重失眠,看了这个 。
编译器可以找出是c决不会被设置。 b变量可能是因为有人在构造函数被调用后,其他DoSomething的()之前进行设置,但是。 做B私人和编译器可能能够帮助。
编译器可以从DoSomething的()的代码将c声明那里,从来没有初始化告诉。 因为它是本地的,也没有可能性,它在其他地方初始化。
它不能告诉何时或何地,你要调用DoSomething的()。 b是公共成员。 这是完全有可能你会调用方法前初始化其他代码。
成员变量初始化为null或到其默认的原始值,如果他们是原语。
局部变量是不确定的,而不是初始化,您负责设置初始值。 编译器会阻止您使用它们。
因此,b为当在c是未定义类的TestClass被实例初始化。
注:空是不确定的不同。
实际上,你已经确定了在通常试图在编辑发现错误Java的系统更大的孔中的一个/编译时间,而不是运行,因为时间 - 作为公认的答案说 - 这是很难说如果b初始化。
有几个方式来解决这一缺陷。 首先是“默认决赛”。 如果你的成员是最终的,你就必须填补他们与构造 - 它会使用路径分析,以确保每一个可能的路径在总决赛中填充(你仍然可以为其分配“空”,这会破坏的目的,但至少你将不得不承认,你是有意这样做)。
第二种方法是严格的空检查。 您可以通过项目或默认属性打开它在Eclipse中设置。 我相信它会迫使你为null检查你的b.notify()调用它。 这可以迅速得到失控,所以它往往会去一组注解,使事情变得更简单:
一旦你打开严格无效检查和注释变量的类型是“可空”和“NOTNULL”的注释可能有不同的名称,但在概念上。 如果你尝试将可空放置到一个非空的变量,你必须首先检查它的空。 参数和返回类型也注释,这样你就不必检查null分配给一个非空变量每一次。
还有一个“NotNullByDefault”包级别的注释,这将使编辑认为没有变量永远不会有一个空值,除非你将其标记为空。
这些注解大多应用在编辑器的水平 - 你可以月食,可能其他编辑器中打开它们 - 这就是为什么他们不一定标准化。 (至少我最后一次检查,爪哇8可能有一些注释我还没有发现尚)