package com.codeartist;import java.util.HashSet;public class ObjectPublish { public static HashSetpersons ; public void init() { persons = new HashSet (); } }
package com.codeartist;import java.util.HashSet;public class ObjectPublish { private HashSetpersons= new HashSet (); public HashSet getPersons() { return this.persons; } }
发布一个对象也会导致此对象的所有非私有的字段对象的发布,其中也包括方法调用返回的对象。在构造函数中使用直接初始化或者调用可改写的实例方法都会导致隐式的this逸出也是经常发生的事情,例如以下代码,在EventListener的实例中也通过this隐含的发布了尚未构造完成的ConstructorEscape实例,可能会造成无法预知的结果。
package com.codeartist;public class ConstructorEscape { public ConstructorEscape(EventSource eventSource) { eventSource.registerListener( new EventListener(){ public void OnEvent(Event e) { doSomeThing(e); } } ); }}
package com.codeartist;public class ConstructorEscape { private final EventListener listener; private ConstructorEscape() { this.listener= new EventListener(){ public void OnEvent(Event e) { doSomeThing(e); } }; } public static ConstructorEscape getInstance(EventSource eventSource) { ConstructorEscape instance = new ConstructorEscape(); eventSource.registerListener(instance.listener); return instance; } }
二、避免对象发布之线程封闭线程封闭可以使数据的访问限制在单个线程之内,相对锁定同步来说,其实实现线程安全比较简单的方式。java提供了ThreadLocal类来实现线程封闭,其可以使针对每个线程存有共享状态的独立副本。其通常用于防止对可变的单实例变量和全局变量进行共享,例如每个请求作为一个逻辑事务需要初始化自己的事务上下文,这个事务上下文应该使用ThreadLocal来实现线程封闭。栈封闭是线程封闭的特例,即数据作为局部变量封闭在执行线程中,对于值类型的局部变量不存在逸出的问题,如果是引用类型的局部变量,开发人员需要确保其不要作为返回值或者其他的关联引用等而被逸出。三、避免对象发布之不变性某个对象创建之后就不能修改其状态,那么我们就说这个对象是不可变对象。由于多线程操作可变状态会导致原子性、可见性一系列问题,所以线程安全性是不可变对象与生俱来的特性。不可变对象由构造函数初始化状态,并可以安全的传递给任何不可信代码使用。所有字段标记为final的对象,由于引用字段的对象可能可以直接修改,所以其并不一定是不可变对象,其需要满足以下条件对象的所有字段都用final标记对象创建之后任何状态都不能修改对象不存在this隐式构造函数逸出
package com.codeartist;import java.util.HashSet;public class ObjectPublish { private HashSetpersons= new HashSet (); public ObjectPublish() { persons.add(new Person("wufengtinghai")); persons.add(new Person("codeartist")); } public Person getPerson(String name) { Person result = null; for(Person p : this.persons) { if(p.name == name) { result = p.Clone(); } } return result; } }