是什么?
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象;
结构
抽象原型类:规定了具体原型对象必须实现的Clone()方法;
具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象;
访问类:使用具体原型类中的clone()方法来复制新的对象;
实现
浅拷贝
创建一个新对象,新对象的属性和原来对象完全相同,基本类型是值传递,但是对于引用类型则仍指向原有属性所指向的对象的内存地址;
Java中的Object类提供了clone方法来实现浅拷贝,我们只需要实现Cloneable接口再重写Object类中的clone方法即可,而这个Cloneable接口实际上就是上面所说的抽象原型类,而这个实现了Cloneable接口的实现类就是具体的原型类:
public class PrototypeCar implements Cloneable{
private String name;
private String no;
private String Engine;
public PrototypeCar(String name, String no, String engine) {
this.name = name;
this.no = no;
Engine = engine;
}
@Override
protected PrototypeCar clone() throws CloneNotSupportedException {
return (PrototypeCar) super.clone();
}
}
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
PrototypeCar tesla = new PrototypeCar("特斯拉", "12", "V12发动机");
System.out.println("原型对象创建完毕,内存地址:"+tesla);
PrototypeCar tesla2 = tesla.clone();
System.out.println("克隆对象创建完毕,内存地址:"+tesla2);
}
}
注意:我们重写clone方法必须要实现Cloneable接口,否则它就会抛CloneNotSupportedException异常:
存在的问题
当我们的原型类中存在其他引用类型,会存在下面这样的问题:
public class Engine {
private String name;
public Engine(String name) {
this.name = name;
}
}
public class PrototypeCar implements Cloneable{
private String name;
private String no;
private Engine Engine;
public PrototypeCar(String name, String no, Engine engine) {
this.name = name;
this.no = no;
this.Engine = engine;
}
克隆后内部的Engine引用对象内存地址还是指向原来的内存地址,并没有帮我们进行克隆,因此面对这种情况我们就要使用深拷贝;
深拷贝
创建一个新对象,属性中引用的其他对象依旧也会被克隆,不再指向原来的内存地址;
我们可以让内部的引用对象的类也去实现Cloneable接口并重写Clone方法,当然如果原型类中的引用对象比较多的话,这种方法明显就很复杂了,重复代码也很多,看起来就很恶心,因此我们一般不用;
我们这里提供一种常用的方案:对象流的方式来实现深拷贝;
首先所有需要拷贝的类都需要实现序列化接口,否则会抛NotSerializableException异常;
public class Engine implements Serializable {
private String name;
public Engine(String name) {
this.name = name;
}
}
public class DeepCloneCar implements Serializable {
private String name;
private String no;
private Engine engine;
通过对象流的方式实现深拷贝:
public DeepCloneCar deepClone() throws IOException, ClassNotFoundException {
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oops = new ObjectOutputStream(baos);
oops.writeObject(this);
//反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
DeepCloneCar car = (DeepCloneCar) ois.readObject();
baos.close();
oops.close();
bais.close();
ois.close();
return car;
}
最终测试一下:
/**
* 原型模式——深拷贝
* */
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Engine engine = new Engine("V12发动机");
DeepCloneCar tesla = new DeepCloneCar("特斯拉", "12", engine);
System.out.println("原型对象创建完毕,内存地址:"+tesla);
System.out.println("内部Enine对象的内存地址:"+tesla.getEngine());
System.out.println("======================================");
DeepCloneCar tesla2 = tesla.deepClone();
System.out.println("克隆对象创建完毕,内存地址:"+tesla2);
System.out.println("内部Engine对象的内存地址:"+tesla2.getEngine());
}
}
总结
当类需要通过原型模式进行拷贝的时候,如果原型类里面的属性没有引用类型,或者引用类型较少,且该类的属性不会频繁修改的情况下,我们可以使用浅拷贝;
但如果引用类型较多,或者后期需要添加或者修改里面的引用类型的属性时,我们选择深拷贝;