一, 原型模式简介
原型模式通过一个对象实例确定新创建对象的种类, 并拷贝创建新的实例; 总的来说, 原型模式就是从一个对象克隆出一个新的对象, 并使这个新对象具有原对象的属性和特征。
实际开发中, 可能需要在原有对象的基础上保留做备用, 并copy一个新对象进行另外分支的代码逻辑处理; 也可能需要在循环中更改原对象地址值以保留现阶段对象, 避免循环进行重复覆盖; 在这种情况下, 都需要对原有对象进行处理, 这时候就需要原型模式的辅助, 原型模式在进行对象copy时, 分为浅拷贝和深拷贝, 下面将分别分析。
二, 原型模式UML图
从图中可以看出,原型模式需要一个向上抽取的ProroType类,并定义一个抽象的clone()方法。具体原型类继承该类并重写clone方法。客户端通过使用具体实现类,调用clone(),来获取copy对象。
JDK中提供了向上的克隆接口Cloneable,具体原型类只需要实现该接口并重写接口方法即可;
三,浅拷贝
浅拷贝是只改变实例地址值,不改变实例所属属性地址值的拷贝方式。
* 引用类型对象 :
public class Person implements Serializable { private String name; private String gender; public Person(String name, String gender) { this.name = name; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } // @Override // public String toString() { // return "Person{" + // "name='" + name + '\'' + // ", gender='" + gender + '\'' + // '}'; // } }
* 具体原型类 :
public class ShallowCopy implements Cloneable{ private String type; private Person person; public String getType() { return type; } public void setType(String type) { this.type = type; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public ShallowCopy(String type, Person person) { this.type = type; this.person = person; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
* Client测试类
public class ShallowCopyTest { public static void main(String[] args) { Person person = new Person("张三", "男"); ShallowCopy shallowCopy = new ShallowCopy("甜瓜", person); try { ShallowCopy copy = (ShallowCopy) shallowCopy.clone(); System.out.println("原值地址 : " + shallowCopy); System.out.println("拷贝值地址 : " + copy); System.out.println("原值TYPE : " + shallowCopy.getType()); System.out.println("拷贝值TYPE : " + copy.getType()); System.out.println("原值PERSON : " + shallowCopy.getPerson()); System.out.println("拷贝值PERSON : " + copy.getPerson()); } catch (Exception e) { e.printStackTrace(); } } }
* 控制台结果
* 对COPY对象属性值进行重新赋值
public static void main(String[] args) { Person person = new Person("张三", "男"); ShallowCopy shallowCopy = new ShallowCopy("甜瓜", person); try { ShallowCopy copy = (ShallowCopy) shallowCopy.clone(); copy.setType("西瓜"); copy.getPerson().setName("李四"); copy.getPerson().setGender("女"); System.out.println("原值地址 : " + shallowCopy); System.out.println("拷贝值地址 : " + copy); System.out.println("原值TYPE : " + shallowCopy.getType()); System.out.println("拷贝值TYPE : " + copy.getType()); System.out.println("原值PERSON : " + shallowCopy.getPerson()); System.out.println("拷贝值PERSON : " + copy.getPerson()); } catch (Exception e) { e.printStackTrace(); } }
* 重新赋值后控制台运行接口
* 浅拷贝总结 :
-- copy后原有对象和copy对象堆内存地址不同,分别为不同对象
-- copy后原有对象和copy对象所属属性对象指向相同的内存地址(实例拷贝,但所属属性没有拷贝)
-- 实例所属属性进行传递遵循JVM规范
四,深拷贝
深拷贝相对与浅拷贝,布置改变了实例对象的内存地址,也改变了所属引用类型属性的内存地址。
* 引用类型对象类, 同上
* 模型类
@Override protected Object clone() throws CloneNotSupportedException { return deepCopy(); } protected Object deepCopy() { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream stream = new ObjectOutputStream(outputStream); stream.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; }
这里通过IO读写操作实现对象深copy
* Client测试类
public static void main(String[] args) { Person person = new Person("张三", "男"); DeepCopy shallowCopy = new DeepCopy("甜瓜", person); try { DeepCopy copy = (DeepCopy) shallowCopy.clone(); System.out.println("原值地址 : " + shallowCopy); System.out.println("拷贝值地址 : " + copy); System.out.println("原值TYPE : " + shallowCopy.getType()); System.out.println("拷贝值TYPE : " + copy.getType()); System.out.println("原值PERSON : " + shallowCopy.getPerson()); System.out.println("拷贝值PERSON : " + copy.getPerson()); } catch (Exception e) { e.printStackTrace(); } }
* 控制台结果
-- 对copy实例所有属性进行重新重新赋值
* Client测试类
public static void main(String[] args) { Person person = new Person("张三", "男"); DeepCopy shallowCopy = new DeepCopy("甜瓜", person); try { DeepCopy copy = (DeepCopy) shallowCopy.clone(); copy.setType("西瓜"); copy.getPerson().setName("李四"); copy.getPerson().setGender("女"); System.out.println("原值地址 : " + shallowCopy); System.out.println("拷贝值地址 : " + copy); System.out.println("原值TYPE : " + shallowCopy.getType()); System.out.println("拷贝值TYPE : " + copy.getType()); System.out.println("原值PERSON : " + shallowCopy.getPerson()); System.out.println("拷贝值PERSON : " + copy.getPerson()); } catch (Exception e) { e.printStackTrace(); } }
* 控制台结果
从控制台结果看出,所属属性地址值在copy后发生变更,对copy对象在新地址上的属性进行重新赋值,不影响原对象
* 深拷贝总结 :
-- 拷贝后copy对象和copy对象属性地址值与原对象完全变更
-- copy对象/原对象值变更/引用变更不互相影响
五,原型模式总结
浅拷贝和深拷贝都生成了一个和原有对象一样的新实例,且两个对象具有不同的地址值。但值得注意的是,浅拷贝后,实例所有引用类型属性在堆内存的地址值没有发生改变,即原有对象和copy对象的所属属性分别指向同一块堆内存,这在开发中容易产生实例内容的关联修改。深拷贝合理的规避了浅拷贝的这一不足,拷贝后对象及对象所属属性的堆内存地址值全都发生了改变,但深拷贝的拷贝逻辑不依赖JDK默认逻辑,需要根据业务逻辑重新编写clone()方法。