内部类、匿名内部类可以访问外部类的对象的域,是因为内部类构造的时候,编译器会把外部类的对象this隐式地作为一个参数传递给内部类的构造方法。
例子:在构造函数中注册一个事件监听器(×)
ThisEscape发布匿名内部类EventListener时,隐含地发布了ThisEscape实例this,而且内部类EventListener实例中包含了对ThisEscape实例地隐含引用,即doSomething(e)方法可以修改 ThisEscape中的属性或者调用ThisEscape中地其他方法。
public class ThisEscape{
public ThisEscape(EventSource source){
source.registerListener(
new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
}
);
}
}
在多线程的环境下,如果A线程正在初始化ThisEscape的实例,在线程A还没完成初始化时,由于共享了构建内部类实例时逸出的外部类ThisEscape的实例this,线程B就可以通过this访问doSomething(e)方法,可能修改ThisEscape的属性,导致一些莫名其妙的现象。
解决问题的关键在于,要保证doSomething(e)方法一定要在外部类ThisEscape初始化完成后才能调用,即不要在构造过程中使this引用逸出。
改进:使用私有的构造函数和一个公共的工厂方法(√)
构造函数将this引用保存到某个地方,使其他线程不会在构造函数完成之前使用它。
public class SafeListener{
private final EventListener listener;
//私有的构造函数
private SafeListener(){
listener = new EventListener(){
public void onEvent(Event e){
doSomething(e);
}
};
}
public static SafeListener newInstance(EventSource source){
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}