继上篇深入浅出设计模式 ------ Prototype(原型模式)的浅克隆实现, 本文进入Prototype(原型模式)的进阶篇----深度克隆。
深度克隆 ---- 序列化方式实现
把对象写到流里的过程是序列化(Serilization)过程,而把对象从流中读出来的过程则叫做反序列化(Deserialization)。写在流里的是对象的一个克隆(新的, 独立的), 而原对象仍存在于JVM内存模型里。因此, 以下代码采用序列化方式实现深度克隆。
第一步: 将上篇的代码做些许改动, 加入对象引用(以便测试浅克隆和深克隆的区别)。
原型接口: FruitPrototype保持不变
package com.wenniuwuren.prototype; /** * 原型接口 * @author wenniuwuren * */ public interface FruitPrototype{ public abstract FruitPrototype shallowClone() throws CloneNotSupportedException; }
package com.wenniuwuren.prototype; /** * 原型具体实现 * @author wenniuwuren * */ public class ConcteteFruitPrototype implements FruitPrototype, Cloneable{ private String size; private String color; private Vitamins vitamins; public ConcteteFruitPrototype(String size, String color, Vitamins vitamins) { this.size = size; this.color = color; this.vitamins = vitamins; } // 克隆 public FruitPrototype shallowClone() throws CloneNotSupportedException { return (FruitPrototype) super.clone(); } // 方便打印 public void display(String colorname) { System.out.println(colorname+"的大小是: "+size+" 颜色是:"+color); } public Vitamins getVitamins() { return vitamins; } public void setVitamins(Vitamins vitamins) { this.vitamins = vitamins; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
package com.wenniuwuren.prototype; import java.util.HashMap; /** * 原型管理类 * @author wenniuwuren * */ public class FruitTool { private HashMap<String, FruitPrototype> fruits = new HashMap<String, FruitPrototype>(); public void put(String key, FruitPrototype fruitPrototype) { fruits.put(key, fruitPrototype); } public FruitPrototype get(String key) { return fruits.get(key); } }
水果都含有的维生素类:Vitamins为新增的对象引用类, 仅提供一个属性方便测试。
package com.wenniuwuren.prototype; /** * 水果都含有的维生素类 * @author wenniuwuren * */ public class Vitamins { private Boolean isContainsVitaminA; public Boolean getIsContainsVitaminA() { return isContainsVitaminA; } public void setIsContainsVitaminA(Boolean isContainsVitaminA) { this.isContainsVitaminA = isContainsVitaminA; } }
测试类:
package com.wenniuwuren.prototype; import java.io.IOException; public class Client { public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException { FruitTool fruitTool = new FruitTool(); // 初始化水果的大小和颜色 fruitTool.put("Apple", new ConcteteFruitPrototype("Middle", "Green", new Vitamins())); fruitTool.put("Watermelon", new ConcteteFruitPrototype("Large", "Red", new Vitamins())); fruitTool.put("Lemon", new ConcteteFruitPrototype("Small", "Yellow", new Vitamins())); System.out.println("#######################浅克隆测试########################"); String fruitName = "Apple"; ConcteteFruitPrototype concteteFruitPrototype1 = (ConcteteFruitPrototype) fruitTool .get(fruitName).shallowClone(); concteteFruitPrototype1.display(fruitName); System.out.print("赋值前, 浅克隆后和浅克隆前的String类型数据相等:" ); System.out.println(concteteFruitPrototype1.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); System.out.print("赋值前, 浅克隆后和浅克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype1.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); System.out.println("----------------分别对克隆后对象赋值, 观察数据是否独立--------------------"); concteteFruitPrototype1.setColor("Red"); System.out.print("赋值后,浅克隆后和浅克隆前的String类型数据相等:"); System.out.println(concteteFruitPrototype1.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); concteteFruitPrototype1.getVitamins().setIsContainsVitaminA(Boolean.FALSE); System.out.print("赋值后, 浅克隆后和浅克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype1.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); } }
测试结果:
Apple的大小是: Middle 颜色是:Green ---------我是邪恶的分割线------------------ 赋值前, 浅克隆后和浅克隆前的String类型数据相等:true 赋值前, 浅克隆后和浅克隆前的 对象引用相等:true ----------------分别对克隆后对象赋值, 观察数据是否独立-------------------- 赋值后,浅克隆后和浅克隆前的String类型数据相等:false 赋值后, 浅克隆后和浅克隆前的 对象引用相等:true可以看出浅克隆确实如上篇所提到的 只负责克隆按值传递的数据(String类型的数据确实是克隆过去了, 已经和旧的String数据独立开了),而不复制它所引用的对象(对象引用还是只是保存在JVM内存模型中的同一份, 其中一个引用数据更改后, 引用数据还是保持一致)。
第二步 :加入深度克隆
原型接口 : 添加深度克隆方法
package com.wenniuwuren.prototype; import java.io.IOException; /** * 原型接口 * * @author wenniuwuren * */ public interface FruitPrototype { // 浅度克隆 public abstract FruitPrototype shallowClone() throws CloneNotSupportedException; // 深度克隆 public FruitPrototype deepClone() throws IOException, ClassNotFoundException; }
package com.wenniuwuren.prototype; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 原型具体实现 * @author wenniuwuren * */ //深度克隆--实现序列化不可少, 作为传输数据的标识 public class ConcteteFruitPrototype implements FruitPrototype, Cloneable, Serializable{ private static final long serialVersionUID = 508114335347191627L; private String size; private String color; private Vitamins vitamins; public ConcteteFruitPrototype(String size, String color, Vitamins vitamins) { this.size = size; this.color = color; this.vitamins = vitamins; } // 浅克隆 public FruitPrototype shallowClone() throws CloneNotSupportedException { return (FruitPrototype) super.clone(); } // 深克隆 @Override public FruitPrototype deepClone() throws IOException, ClassNotFoundException { //将对象序列化到流里 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //将流反序列化回来 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (FruitPrototype)ois.readObject(); } // 方便打印 public void display(String colorname) { System.out.println(colorname+"的大小是: "+size+" 颜色是:"+color); } public Vitamins getVitamins() { return vitamins; } public void setVitamins(Vitamins vitamins) { this.vitamins = vitamins; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
package com.wenniuwuren.prototype; import java.util.HashMap; /** * 原型管理类 * @author wenniuwuren * */ public class FruitTool { private HashMap<String, FruitPrototype> fruits = new HashMap<String, FruitPrototype>(); public void put(String key, FruitPrototype fruitPrototype) { fruits.put(key, fruitPrototype); } public FruitPrototype get(String key) { return fruits.get(key); } }
package com.wenniuwuren.prototype; import java.io.Serializable; /** * 水果都含有的维生素类 * @author wenniuwuren * */ // 深度克隆--实现序列化不可少, 作为传输数据的标识 public class Vitamins implements Serializable{ private static final long serialVersionUID = 1183447243521084226L; private Boolean isContainsVitaminA; public Boolean getIsContainsVitaminA() { return isContainsVitaminA; } public void setIsContainsVitaminA(Boolean isContainsVitaminA) { this.isContainsVitaminA = isContainsVitaminA; } }
package com.wenniuwuren.prototype; import java.io.IOException; public class Client { public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException { FruitTool fruitTool = new FruitTool(); // 初始化水果的大小和颜色 fruitTool.put("Apple", new ConcteteFruitPrototype("Middle", "Green", new Vitamins())); fruitTool.put("Watermelon", new ConcteteFruitPrototype("Large", "Red", new Vitamins())); fruitTool.put("Lemon", new ConcteteFruitPrototype("Small", "Yellow", new Vitamins())); System.out.println("#######################浅克隆测试########################"); String fruitName = "Apple"; ConcteteFruitPrototype concteteFruitPrototype1 = (ConcteteFruitPrototype) fruitTool .get(fruitName).shallowClone(); concteteFruitPrototype1.display(fruitName); System.out.print("赋值前, 浅克隆后和浅克隆前的String类型数据相等:" ); System.out.println(concteteFruitPrototype1.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); System.out.print("赋值前, 浅克隆后和浅克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype1.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); System.out.println("----------------分别对克隆后对象赋值, 观察数据是否独立--------------------"); concteteFruitPrototype1.setColor("Red"); System.out.print("赋值后,浅克隆后和浅克隆前的String类型数据相等:"); System.out.println(concteteFruitPrototype1.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); concteteFruitPrototype1.getVitamins().setIsContainsVitaminA(Boolean.FALSE); System.out.print("赋值后, 浅克隆后和浅克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype1.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); System.out.println("#######################深克隆测试########################"); fruitName = "Watermelon"; ConcteteFruitPrototype concteteFruitPrototype2 = (ConcteteFruitPrototype) fruitTool .get(fruitName).deepClone(); concteteFruitPrototype2.display(fruitName); System.out.print("赋值前, 深克隆后和深克隆前的String类型数据相等:" ); System.out.println(concteteFruitPrototype2.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); System.out.print("赋值前, 深克隆后和深克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype2.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); System.out.println("----------------分别对克隆后对象赋值, 观察数据是否独立--------------------"); concteteFruitPrototype2.setColor("Yellow"); System.out.print("赋值后,深克隆后和深克隆前的String类型数据相等:"); System.out.println(concteteFruitPrototype2.getColor().equals(((ConcteteFruitPrototype)fruitTool .get(fruitName)).getColor())); concteteFruitPrototype2.getVitamins().setIsContainsVitaminA(Boolean.FALSE); System.out.print("赋值后, 深克隆后和深克隆前的 对象引用相等:" ); System.out.println(concteteFruitPrototype2.getVitamins().getIsContainsVitaminA() == ((ConcteteFruitPrototype)fruitTool .get(fruitName)).getVitamins().getIsContainsVitaminA()); } }
测试结果 :
#######################浅克隆测试######################## Apple的大小是: Middle 颜色是:Green 赋值前, 浅克隆后和浅克隆前的String类型数据相等:true 赋值前, 浅克隆后和浅克隆前的 对象引用相等:true ----------------分别对克隆后对象赋值, 观察数据是否独立-------------------- 赋值后,浅克隆后和浅克隆前的String类型数据相等:false 赋值后, 浅克隆后和浅克隆前的 对象引用相等:true #######################深克隆测试######################## Watermelon的大小是: Large 颜色是:Red 赋值前, 深克隆后和深克隆前的String类型数据相等:true 赋值前, 深克隆后和深克隆前的 对象引用相等:true ----------------分别对克隆后对象赋值, 观察数据是否独立-------------------- 赋值后,深克隆后和深克隆前的String类型数据相等:false 赋值后, 深克隆后和深克隆前的 对象引用相等:false
至此, 深入浅出设计模式系列的创建型模式就结束了, 接下来年前工作繁忙, 要改一个开源代码, 时间不多, 尽量挤时间写其他的模式, 希望博友们多提意见, 有助于笔者提高博客质量。 (晚安, 我爱这个世界)