原型模式就是从一个对象再创建另外一个可定制的对象
, 而且不需要知道任何创建的细节。
所谓原型模式, 就是 Java 中的克隆技术
, 以某个对象为原型。 复制出新的对象。 显然新的对象具备原型对象的特点, 效率高(避免了重新执行构造过程步骤)
在介绍原型模式之前,需要介绍浅拷贝和深拷贝的概念
1.浅拷贝和深拷贝的特点
浅拷贝
:
- ①对于数据类型是
基本数据类型
的成员变量,浅拷贝会直接进行值传递
,也就是将该属性值复制一份给新的对象。 - ②对于数据类型是
引用数据类型的成员变量
,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递
,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
深拷贝
:
- ①复制对象的所有
基本数据类型的成员变量值
- ②为所有引用数据类型的成员变量申请存储空间,并
复制每个引用数据类型成员变量所引用的对象
,直到该对象可达的所有对象
。也就是说,对象进行深拷贝要对整个对象图
进行拷贝!
总结
:
深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间
;而浅拷贝只是传递地址指向
,新的对象并没有对引用数据类型创建内存空间
2.原型模式的浅拷贝和深拷贝的实现方式
- 浅拷贝
通过拷贝构造方法
实现浅拷贝
通过重写clone()方法
进行浅拷贝 - 深拷贝
通过重写clone方法
来实现深拷贝
通过对象序列化
实现深拷贝
接下来直接介绍浅拷贝和深拷贝的具体实现
在此之前,先给出原型模式的uml图
原型模式.png
3.原型模式的浅拷贝
浅度克隆目标的引用对象
/**
* @Project: spring
* @description: 浅度克隆目标的引用对象
* @author: sunkang
* @create: 2018-09-02 10:48
* @ModificationHistory who when What
**/
public class CloneableTarget {
public String cloneName;
public String cloneClass;
public CloneableTarget( String cloneName,String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
}
浅度克隆的对象的两种实现方式
/**
* @Project: spring
* @description: 克隆对象的浅拷贝
* 1.通过构造方法来实现浅度拷贝
* 2.通过clone()方法来实现浅度拷贝
* @author: sunkang
* @create: 2018-09-02 10:47
* @ModificationHistory who when What
**/
public class Prototype implements Cloneable {
public String name ;
//拷贝的引用类型的成员变量
public CloneableTarget cloneableTarget;
public Prototype(){
}
//1.通过构造方法来实现浅度拷贝
public Prototype(Prototype prototype) {
this.name = prototype.name;
this.cloneableTarget = prototype.cloneableTarget;
}
//2.通过clone()方法来实现浅度拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
浅克隆的测试
/**
* @Project: spring
* @description: 浅度克隆对象的测试
*
* @author: sunkang
* @create: 2018-09-02 10:51
* @ModificationHistory who when What
**/
public class CloneTest {
public static void main(String[] args) {
Prototype p = new Prototype();
p.name="kang";
p.cloneableTarget = new CloneableTarget("clone","CloneableTarget");
System.out.println("原有的对象:"+p);
System.out.println("原有的值对象引用:"+p.name.hashCode());
System.out.println("原有的引用类型对象:"+p.cloneableTarget);
try {
//方式一 :通过重写clone()方法进行浅拷贝
Prototype clonePrototype = (Prototype) p.clone();
System.out.println("重写clone()克隆的对象:"+clonePrototype);
System.out.println("重写clone()克隆的值对象引用:"+p.name.hashCode());
System.out.println("重写clone()克隆的引用类型对象:"+ clonePrototype.cloneableTarget);
//通过修改引用对象String类型的值,发现原有的对象的值没有发生改变,因为String对象是不可变对象,放在常量池中的,无法修改的
//String 可以比较特殊,可以看做是值传递
clonePrototype.name ="sun";
System.out.println("原有对象的name:"+p.name);
System.out.println("修改过的clone对象的name:"+clonePrototype.name);
//方式二: 通过拷贝构造方法实现浅拷贝
Prototype constructClone= new Prototype(p);
System.out.println("构造方法克隆的对象:"+constructClone);
System.out.println("构造方法克隆的值对象引用:"+constructClone.name.hashCode());
System.out.println("构造方法克隆的引用类型对象:"+ constructClone.cloneableTarget);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
测试结果 : 可以发现引用类型的目标对象为同一个引用,string 对象可以看做是值传递,string对象是不可变的,克隆对象修改后的值对原有对象的值没有影响
浅克隆测试结果.png
4.原型模式的深拷贝
深度拷贝的目标引用对象
/**
* 深度拷贝的目标引用对象
*/
public class DeepCloneableTarget implements Serializable,Cloneable {
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
深度拷贝的实现拷贝的两种方式
/**
* @Project: spring
* @description: 深度拷贝的实现拷贝的两种方式
* @author: sunkang
* @create: 2018-09-02 11:19
* @ModificationHistory who when What
**/
public class DeepPrototype implements Serializable,Cloneable {
public String name ;
public DeepCloneableTarget deepCloneableTarget;
public DeepPrototype(){
}
//方式1 :通过重写clone方法来实现深拷贝 (引用对象多,这种方法比较繁琐)
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
try {
deep = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
DeepPrototype deepPrototype = (DeepPrototype) deep;
deepPrototype.deepCloneableTarget = (DeepCloneableTarget) deepPrototype.deepCloneableTarget.clone();
return deepPrototype;
}
//方式2: 通过对象序列化实现深拷贝 (推荐)
public Object deepClone(){
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if(oos !=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bos !=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//输入流关闭省略
}
return null;
}
}
深度拷贝的测试
/**
* @Project: spring
* @description: 深度拷贝的测试
* @author: sunkang
* @create: 2018-09-02 11:38
* @ModificationHistory who when What
**/
public class DeepCloneTest {
public static void main(String[] args) {
DeepPrototype p = new DeepPrototype();
p.name="kang";
p.deepCloneableTarget = new DeepCloneableTarget("clone","CloneableTarget");
System.out.println("原有的对象:"+p);
System.out.println("原有的值对象引用:"+p.name.hashCode());
System.out.println("原有的引用类型对象:"+p.deepCloneableTarget);
try {
//方式一 :通过重写clone()方法进行浅拷贝
DeepPrototype clonePrototype = (DeepPrototype) p.clone();
System.out.println("clone()方法克隆的对象:"+clonePrototype);
System.out.println("clone()方法克隆的值对象引用:"+p.name.hashCode());
System.out.println("clone()方法克隆的引用类型对象:"+ clonePrototype.deepCloneableTarget);
//方式二: 通过对象序列化实现深拷贝
DeepPrototype serializableClone= (DeepPrototype) p.deepClone();
System.out.println("序列化方法克隆的对象:"+serializableClone);
System.out.println("构序列化方法克隆的值对象引用:"+serializableClone.name.hashCode());
System.out.println("序列化方法克隆的引用类型对象:"+ serializableClone.deepCloneableTarget);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
测试结果 :可以发现引用类型的成员变量的地址都是不一样
的了,说明实现了深度拷贝
深拷贝的测试结果.png