继上篇深入浅出设计模式 ------ 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;
}
}
原型管理类: FruitTool 保持不变
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
可以从测试结果清楚看出, 深度克隆与浅度克隆的 最大区别便是深度克隆把对象引用一起克隆了一份。 所以结果中修改引用内数据将导致引用数据前后不一致。
至此, 深入浅出设计模式系列的创建型模式就结束了, 接下来年前工作繁忙, 要改一个开源代码, 时间不多, 尽量挤时间写其他的模式, 希望博友们多提意见, 有助于笔者提高博客质量。 (晚安, 我爱这个世界)