原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。(百度百科)
简单的讲,就是为了创建一个对象,复制一个原有的对象,然后为这个新对象开辟内存空间。
在Jdk中提供了复制对象的接口,只需要实现Cloneable接口并重写clone()方法(也可以不实现Cloneable接口,因为Object中默认提供clone()方法:
protected native Object clone() throws CloneNotSupportedException;
),就可以使用clone()方法了。
public class ProtoTypePattern implements Cloneable{ public ProtoTypePattern() { System.out.println("构造函数执行了"); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } public static void main(String[] args) { ProtoTypePattern protoType = new ProtoTypePattern(); try { ProtoTypePattern clone = (ProtoTypePattern)protoType.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
如果不实现Cloneable接口,程序会抛出异常:
java.lang.CloneNotSupportedException: com.example.patterns.protoTypePattern.ProtoTypePattern at java.lang.Object.clone(Native Method) at com.example.patterns.protoTypePattern.ProtoTypePattern.clone(ProtoTypePattern.java:6) at com.example.patterns.protoTypePattern.ProtoTypePattern.main(ProtoTypePattern.java:12)
使用clone()方法建造的新对象是不执行构造函数的,是从内存中直接开辟一块空间以二进制的方式进行拷贝。构造函数只执行了一次,程序运行结果如下。
Connected to the target VM, address: '127.0.0.1:52677', transport: 'socket'
构造函数执行了
Disconnected from the target VM, address: '127.0.0.1:52677', transport: 'socket'
以上就是原型模式的实现。
但还不够,因为还涉及到一个浅克隆和深克隆的概念。
Object默认的clone()实现方式是浅克隆,一个对象中只有基本数据类型和String类型(String类型不可更改)会克隆,其他的引用对象或数组引用则会指向原有对象。当一处修改对象时,两边都会变化。
解决办法是让克隆对象中的引用对象实现Cloneable接口,将引用对象的克隆值作为克隆对象新的引用对象。
还有一个解决办法是使用串行化,使用字节数组流将对象写入再读出来。这样做必须写入的对象及引用的对象都是可串行的,否则就需要将不可串行化的对象用transient修饰。
public Object deepClone() { //将对象写到流里 ByteArrayOutoutStream 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()); }