对象的发布
“发布对象”指的是使对象能够在当前作用于之外的代码中使用
例如,将一个指向该对象的引用保存到其他代码可以访问的地方(情况一),或者在某一个非私有的方法中返回该引用(情况二),或者将引用传递到其他类的方法中(情况三)。
//情况一
public class Person(){
private String name;
private int age;
}
public class Record{
Person p;
}
//情况二
public class Person(){
private String name;
private int age;
public Person get(){
return new Person();
}
}
//情况三
public class Person(){
private String name;
private int age;
}
public class Record{
private void getPersonMessage(Person p){
.....
}
}
对象逸出
当某个不应该发布的对象被发布时,这种情况就被称为“对象逸出”。
常见的对象逸出:
- 内部的可变状态逸出
class UnsafeStates{
private String[] states = {"AK","AL",...};
public String[] getStates(){
return states;
}
}
如上,数组states本事私有的变量,但是以public公有的方式发布出去,导致states已经逸出了它所在的作用域,任何调用者都能修改这个数组的内容。
- 隐式地使this引用逸出
public class ThisEscape{
public ThisEscape(EventSource source){
source.registerListener(
new EventListener(){
public void onEvent(Event e){
dosomething(e);
}
})
}
}
实际上,这里是需要发布内部类实例,而这样的做法导致当ThisEscape发布EventListener时,也隐含地发布了ThisEscape实例本身。因为在这个内部类的实例中包含了对ThisEscape实例的隐含引用。
这样的逸出会出现什么问题呢?
public class ThisEscape{
private int count;
public ThisEscape(EventSource source){
source.registerListener(
new EventListener(){
public void onEvent(Event e){
dosomething(e);
}
})
//在这里count初始化为1
count = 1;
}
}
举个例子,如上,我们知道this逸出会导致ThisEscape也发布出去,也就是ThisEscape还没有构建完成就发布出去,也就是count=1;
这一句还没执行就发布了ThisEscape对象,如果要使用count时,很有可能会出现对象不一致的状态。
那么如何解决这个问题呢?
使用工厂方法来防止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;
}
}
如此,便能保证在对象为构造完成之前,是不会发布该对象。