关于克隆,我们会想到《西游记》里面孙悟空的猴毛分身,也会想到《火影忍者》里面的影分身之术。他们都是用一个物体复制若干个一模一样的物体。在面向对象的系统中,我们也可以通过克隆来复制一些对象—也就是我们所说的原型模式
用原型实例指定创建对象的种类,通过拷贝这些原型创建新的对象,也就是利用一个原型对象来指明我们要创建对象的类型,然后通过复制这个对象来获取一模一样的对象实例
一个简单的原型模式Demo
1 2 3 4 5 6 7 8 9 10 11 12
| public class PrototypeClass implements Cloneable{ @Override protected PrototypeClass clone(){ PrototypeClass prototypeClass = null; try { prototypeClass = (PrototypeClass)super.clone(); }catch (CloneNotSupportedException e){
} return prototypeClass; } }
|
- 实现Cloneable接口,在JVM中只有具有这个标记的对象才有可能被拷贝
- 重写覆盖Clone()方法
为什么要用原型模式
- 性能优良。 原型模式是在内存二进制流的拷贝,比直接new一个对象性能要好得多。所以特别是要在一个循环体内产生大量对象的时候,原型模式更好体现其优点
- 避开构造函数的束缚(既是优点也是缺点)
直接在内存中拷贝,构造函数是不会执行的
原型模式应用场景
- 资源优化场景
- 性能和安全要求场景
- 一个对象多个修改者的场景
两种拷贝方式
浅拷贝
只拷贝本对象,对象内部的数组、引用对象都不拷贝,还是指向原生对象的内部元素地址。原始类型(int、long、char)以及String都会被拷贝
如何才能保证成员变量不被拷贝(保证以下两个条件)
- 必须是类的成员变量,而不是方法内变量
- 必须是一个可变的引用对象,而不是一个原始类型或者不可变对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Thing implements Cloneable { private List<String> list = new ArrayList<String>();
@Override protected Thing clone(){ Thing thing = null; try { thing = (Thing)super.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return thing; } public void setValue(String value){ this.list.add(value); } public List<String> getValue(){ return this.list; } }
|
浅克隆是不安全的方式,两个对象共享了一个私有变量,大家都能够进行修改。
深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class DeepThing implements Cloneable { private ArrayList<String> list = new ArrayList<String>();
@Override protected DeepThing clone(){ DeepThing thing = null; try { thing = (DeepThing)super.clone(); this.list = (ArrayList<String>)this.list.clone(); }catch (CloneNotSupportedException e){ e.printStackTrace(); } return thing; }
public void setValue(String value){ this.list.add(value); }
public List<String> getValue(){ return this.list; } }
|
总结
- 原型模式向客户隐藏了创建对象的复杂性,客户只需要知道创建对象类型,就可以获得对象一模一样的新对象。
- 有两种克隆方式:深克隆、浅克隆
- 浅克隆;不安全,对象公有私有变量
- 有时对象的复制可能会比较复杂