原型模式:使用原型实例指定待创建对象的类型,并通过这个原型进行自我复制创建一个相同类型的对象。通过原型模式创建的对象是一个全新的对象,它和原型对象具有相同的内容,但是在内存中的地址值不同,修改其中一个对象的属性,另一个对象不会发生变化。根据原型对象赋值的时候是否复制了其引用数据类型的成员变量,分为浅克隆和深克隆。
浅克隆:如果原型对象的成员变量是值类型,那么将值传递给克隆对象,如果原型对象的成员变量是地址值类型,那么将地址值传递给克隆对象,两个对象的该成员变量指向堆中的同一个对象,即原型对象对于引用数据类型,没有对它成员变量进行复制。参照下面代码中的clone()方法,虽然对wukong进行了克隆,生成了一个分身,他们具有相同的name和tall,但却用着同一个jingubang,这明显是不合理的,需要对他本身的jingubang也进行复制。由于浅克隆调用的是Object的clone()方法,这是一个nativce方法,默认复制值和引用数据类型的地址值,因此会出现这个问题,如果必须要解决这个问题,那么要采用深克隆。
深克隆:不管原型对象的成员变量是值类型还是引用数据类型,都对他们进行复制,克隆对象复制的引用类型成员变量和原型的引用成员变量的地址值不同。整理了三种深克隆的方法。
-
第一种是继续使用Object的clone()方法,对于对象中的每个引用数据类型,都给他们实现自己的clone()方法,参考下面示例中的deepclone1(),优点是简单方便,缺点是每次对类进行修改,都可能要重新编写clone()方法,违反了开闭原则。
-
第二种方法是采用了第三方类JSONObject的序列化和反序列化,参考下面示例中的deepclone2()。
-
第三种方法是采用了将对象通过流的序列化和反序列化,参考下面示例中的deepclone3()。
package prototype.general; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import com.alibaba.fastjson.JSONObject; public class Wukong implements Cloneable, Serializable{ private static final long serialVersionUID = 1L; private String name; private Integer tall; private JinGuBang jingubang; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getTall() { return tall; } public void setTall(Integer tall) { this.tall = tall; } public JinGuBang getJingubang() { return jingubang; } public void setJingubang(JinGuBang jingubang) { this.jingubang = jingubang; } public Wukong clone() { Wukong wukong = null; try { wukong = (Wukong) super.clone(); return wukong; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public Wukong deepClone1() { Wukong wukong = null; try { wukong = (Wukong) super.clone(); JinGuBang jingubang = wukong.getJingubang().clone(); wukong.setJingubang(jingubang); return wukong; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } public Wukong deepClone2() { String wukongString = JSONObject.toJSONString(this); Wukong fenshen3 = JSONObject.parseObject(wukongString, Wukong.class); return fenshen3; } public Wukong deepClone3() throws IOException, ClassNotFoundException { ByteArrayOutputStream bao = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bao); oos.writeObject(this); ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray()); ObjectInputStream ois=new ObjectInputStream(bis); return (Wukong)ois.readObject(); } }
package prototype.general; import java.io.Serializable; public class JinGuBang implements Cloneable, Serializable{ private static final long serialVersionUID = 1L; private Double width; private Long height; public Double getWidth() { return width; } public void setWidth(Double width) { this.width = width; } public Long getHeight() { return height; } public void setHeight(Long height) { this.height = height; } public JinGuBang clone() { JinGuBang jingubang = null; try { jingubang = (JinGuBang) super.clone(); return jingubang; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
package test;
import java.io.IOException;
import prototype.general.JinGuBang;
import prototype.general.Wukong;
public class Test {
public static void main(String[] args) throws ClassNotFoundException, IOException {
System.out.println("测试浅克隆");
Wukong wukong = new Wukong();
wukong.setName("孙悟空");
wukong.setTall(180);
JinGuBang jingubang = new JinGuBang();
jingubang.setWidth(1.00);
jingubang.setHeight(100L);
wukong.setJingubang(jingubang);
System.out.println("测试浅克隆");
Wukong fenshen1 = wukong.clone();
System.out.println(wukong);
System.out.println(fenshen1);
System.out.println(wukong.getName() == fenshen1.getName());
System.out.println(wukong.getTall() == fenshen1.getTall());
System.out.println(wukong.getJingubang() == fenshen1.getJingubang());
System.out.println("----------------------------------");
System.out.println("测试深克隆----方法一");
Wukong fenshen2 = wukong.deepClone1();
System.out.println(wukong);
System.out.println(fenshen2);
System.out.println(wukong.getName() == fenshen2.getName());
System.out.println(wukong.getTall() == fenshen2.getTall());
System.out.println(wukong.getJingubang() == fenshen2.getJingubang());
System.out.println("测试深克隆----方法二");
Wukong fenshen3 = wukong.deepClone2();
System.out.println(wukong);
System.out.println(fenshen3);
System.out.println(wukong.getName() == fenshen3.getName());
System.out.println(wukong.getTall() == fenshen3.getTall());
System.out.println(wukong.getJingubang() == fenshen3.getJingubang());
System.out.println("测试深克隆----方法三");
Wukong fenshen4 = wukong.deepClone3();
System.out.println(wukong);
System.out.println(fenshen4);
System.out.println(wukong.getName() == fenshen4.getName());
System.out.println(wukong.getTall() == fenshen4.getTall());
System.out.println(wukong.getJingubang() == fenshen4.getJingubang());
}
}
测试浅克隆
prototype.general.Wukong@7852e922
prototype.general.Wukong@4e25154f
true
true
true
----------------------------------
测试深克隆----方法一
prototype.general.Wukong@7852e922
prototype.general.Wukong@70dea4e
true
true
false
测试深克隆----方法二
prototype.general.Wukong@7852e922
prototype.general.Wukong@7c53a9eb
false
false
false
测试深克隆----方法三
prototype.general.Wukong@7852e922
prototype.general.Wukong@1c6b6478
false
false
false
通过输出结果可以明显看书,深度克隆方法一二三都完成了深克隆的功能,但是方法一的基本数据类型的比较返回都是true,方法二三的基本数据类型比较都是false,其中应该是序列化和反序列化过程中产生的问题,将在另一篇博客中通过fastJson的解析中进行分析。
总结:
优点:可以快速得到大量相同的对象,且不用通过创建工厂类;克隆对象能够保证对象的状态;对于创建过程复杂的对象,能够节省资源。
缺点:每个对象都要实现自己的clone方法,当类要修改的时候,clone方法可能也要修改;克隆对象要实现Cloneable或者Serializable接口,多重嵌套情况下比较麻烦。
适应化境:创建对象成本比较高;要保存当前对象的状态;一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值。