发布:使对象能够在当前作用域之外的代码中使用
逸出:当某个不该被发布的对象被发布时,这种情况称为逸出
发布内部状态将会破坏封装性,并使得程序难以维持不变性条件
当某个对象逸出后,必须对程序进行分析,以便找出哪些对象或线程可能会误用该对象,这正是使用封装的最主要原因:使对程序的正确性分析变为可能,并使无意中破坏设计约束条件变得更难
无论其它的线程会对逸出的对象引用执行何种操作,都不重要,因为误用该引用的风险始终是客观存在的
对象的发布有如下几种方式:
一,将对象的引用保存在公有变量或公有静态变量中
public class Test { public static List<People> list; public Test(){ list = new ArrayList<People>(); } }
通过list对象可以轻易的遍历list对象中保存的People对象,实际上list对象中保存的People对象也被间接的发布了
一个已经发布的对象能够通过非私有的变量引用或方法调用到达其他的对象,那么这些对象也会被发布
二,在一个非私有的方法中返回该引用
public class Test { private String[] strs = {"AB","BA"}; public String[] getStrs() { return strs; } }
通过getStrs()方法可以获得本来应该被封装在Test类内部的strs数组
三,将对象引用传递给外部方法
外部方法:对当前类来说,外部方法是指行为不完全由当前类来规定的方法,包括其他类中定义的方法以及当前类中可以被改写的方法(既不是私有方法,也不是final方法)
当把一个对象传递给外部方法时,就相当于发布了这个对象
public class Test { public void get(Object obj){ //obj对象逸出 ... } }
this引用逸出:
一,内部类中this引用逸出
在内部类中保存了一个指向创建该内部类的外围类的引用,所以内部类中可以使用创建该内部类的外围类的私有属性、方法
public class Outer { private String str="Outer's string"; public class Inner{ public void write(){ System.out.println(Outer.this.str); } } public static void main(String[] args) { Outer out = new Outer(); Outer.Inner in = out.new Inner(); in.write(); } }
执行后的结果是:
Outer's string
当Inner类被发布的同时,Outer类也会被发布
二,构造函数中this引用逸出
public class ThisEscape { private Thread t; public ThisEscape(){ System.out.println(this); t = new Thread(){ public void run(){ System.out.println(ThisEscape.this); } }; t.start(); // do something } public static void main(String[] args){ ThisEscape a = new ThisEscape(); } }
运行结果:
ThisEscape@61de33 ThisEscape@61de33
this引用被线程t共享,故线程t的发布将导致ThisEscape对象的发布,由于ThisEscape对象被发布时还未构造完成,这将导致ThisEscape对象逸出(在构造函数中创建线程是可以的,但是不要在构造函数执行完之前启动线程)
在构造函数中调用一个可改写的实例方法时,也会导致同样的问题
解决方式(私有构造函数+公有工厂方法):
public class ThisEscape { private final Thread t; private ThisEscape(){ t = new Thread(){ public void run(){ // do something } }; // do something } public static ThisEscape getInstance(){ ThisEscape thisEscape = new ThisEscape(); thisEscape.t.start(); return thisEscape; } }