发布(Publishing)一个对象,意味着对象能够在当前作用域之外的代码中使用。例如,将一个指向该对象的引用保存到其它代码可以访问的地方,或者在某一个非私有的方法中返回该引用,或者将引用传递到其它类的方法中。
在许多情况下,我们要确保对象及其内部状态不被发布。在其它情况下,我们又需要发布某个对象,但如果在发布时要确保线程安全性,则可能需要同步。
发布内部状态可能会破坏封装性,并使得程序难以维持不变性条件。例如,如果在对象构造完成之前就发布该对象,就会破坏线程安全性。
逸出(Escaped),是指某个不应该发布的对象被发布。
发布对象的最简单方法是将对象的引用保存到一个公有的静态变量中,以便任何类和线程都能看见该对象。
当发布某个对象时,可能会间接地发布其他对象(Publishing one object may indirectly publish others.)。如果将一个Secret对象添加到已经发布的集合knownSecrets中,那么同样会发布这个Secret对象,因为任何代码都可以遍历这个集合,并获得对这个新Secret对象的引用。
class UnsafeStates { // states已经逸出了作用域,don't do this.
private String[] states = new String[]{"AK","AL",...};
public String[] getStates() {return states;}
}
发布一个对象时,也会发布该对象的非私有域中引用的任何对象。一般来说,如果一个已经发布的对象能够通过非私有的变量引用和方法调用到其它的对象,那么这些对象也都会被发布。
无论其它的线程会对已发布的引用执行何种操作,其实都不重要,因为误用该引用的风险始终存在。当某个对象逸出后,你必须假设有某个类或线程可能会误用该对象。这正是需要封装的最主要原因:封装能够使得对程序的正确性进行分析变得可能,并使得无意中破坏设计约束条件变得更难。
最后一种发布对象或其内部状态的机制就是发布一个内部的类实例。
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(
new EventListener() {
public void onEvent(Event e) {
// doSomething
}
}
);
}
}
安全的对象构造实践(Safe construction practices)。在ThisEscape中给出了逸出的一个特殊示例,即this引用在构造函数中逸出。
Do not allow the this reference to escape during construction.