在上篇博文中我们已经简单的阐述了大部分有关单例(Singleton)模式的知识点,主要包括何谓单例、单例使用场景、单例几种实现方式及其特点以及单例的局限性。接下来我们将着重阐述有关原型(Prototype)的一些常识,主要涉及原型的用意、实现形式,以及与之相关的clone条件与equals方法和深浅拷贝等问题。
所谓原型(Prototype)模式,是指通过给出一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的办法创建出更多同类型的对象。下图左侧是原型模式的一般性结构示意图,右侧是简单的实现样例。
原型(Prototype)模式主要采用的是java的clone克隆,克隆有克隆的前提(即满足的条件),也有深、浅之别。
首先,我们来看看克隆满足的条件:1、对任何的对象x,都有 x.clone() != x。即克隆对象与原对象不是同一个对象。2、对任何的对象x,都有 x.clone().getClass() == x.getClass(),即克隆对象与原对象的类型是一样。3、如果对象x的equals()方法定义恰当的话,那么 x.clone().equals(x)应当是成立的。 在java语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件。(一般来说,clone前两个条件必须,后一个可选)。
其次,我们来看看深浅克隆(即拷贝)的问题。浅克隆是指仅仅克隆所考虑的对象,而不克隆它所引用的对象,而深克隆是指把要克隆的对象及其所引用的对象都克隆一遍,这种对引用到的对象的克隆称之为间接克隆。(注:深克隆要深入到多少层是一个不易确定的问题)。
最后,我们再共同关注下克隆满足条件之3equals()方法。关于此条件的满足,很多人误认为可以通过继承得到java.lang.Object对象的equals()方法就足够了,这其实是一个误区点。在java的源码中java.lang.Object的equals()方法是这样的:
public boolean equals(Object obj) {
return (this == obj);
}
也就是说,这仅仅是地址空间而非对象内容的比较,当两个变量同时指向一个对象时,equals()方法才会返回true。很显然,这并不是适合所有需要被克隆的对象的。
一般而言,为了做到深克隆,所有对象都需要实现java.Serializable这个串行化接口。在这里,我们简单地提一下利用序列化的方法做深克隆。
记得在《java与模式》一书中讲到:把对象写到流里的过程是串行化过程(java程序师圈里戏称冷冻或腌咸菜),把对象从流中读出来的并行化过程称解冻或回鲜过程。应当指出,写到流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此,“腌成咸菜”的只是对象的一个拷贝,java咸菜还可以回鲜。实现过程的核心代码大致为:
//deeply clone
public Object clone(){
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
}
关于原型(Prototype)模式,姑且阐述至此!