原型模式
原型模式(Prototype Pattern):一般指的是我们通过一个原型实例,然后创建出和原型实例一样的重复对象,主要就是用来实现对象的克隆。
原型模式的实质就是克隆对象,那么克隆又可以分为浅克隆和深克隆。
浅克隆:指拷贝对象时仅仅拷贝对象本身和对象中的基本变量,但是不拷贝对象包含的引用类型。假如对象中含有一个引用属性,那么拷贝的时候只会把引用属性的地址拷贝过来,这样的缺点就是一旦原型实例对象的引用属性发生了修改,那么克隆过来的对象也会一起变动。
深克隆:不仅拷贝对象本身,而且会将引用对象也一起实现拷贝,这样一旦原型实例中的引用属性发生变化,不会影响到克隆后的对象。
想必大家看概念会很模糊,下边通过代码来实现两种克隆方式,进行比对学习。
示例:
package cn.liangyy.prototype;
/**
* 原型接口
*/
public interface IPrototype {
IPrototype clone(); //克隆方法
}
复制代码
package cn.liangyy.prototype;
import java.util.List;
/**
* 原型实例类
*/
public class ShallowPrototype implements IPrototype {
private String name;
private int age;
private List<String> phoneList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getPhoneList() {
return phoneList;
}
public void setPhoneList(List<String> phoneList) {
this.phoneList = phoneList;
}
@Override
public IPrototype clone() {
ShallowPrototype shallowPrototype = new ShallowPrototype();
shallowPrototype.setAge(this.age);
shallowPrototype.setName(this.name);
shallowPrototype.setPhoneList(this.phoneList);
return shallowPrototype;
}
}
复制代码
package cn.liangyy.prototype;
import java.util.ArrayList;
import java.util.List;
public class TestShallowPrototype {
public static void main(String[] args) {
//初始化一个原型实例对象ShallowPrototype
ShallowPrototype shallowPrototype = new ShallowPrototype();
shallowPrototype.setAge(20);
shallowPrototype.setName("亚索");
List<String> phoneList = new ArrayList<>();
phoneList.add("133xxxx452");
shallowPrototype.setPhoneList(phoneList);
//克隆原型对象
ShallowPrototype cloneShallowPrototype = (ShallowPrototype) shallowPrototype.clone();
//打印结果
System.out.println("克隆前:"+shallowPrototype.getPhoneList());
System.out.println("克隆后:"+cloneShallowPrototype.getPhoneList());
//比较两个对象是否指向同一内存地址
System.out.println(shallowPrototype.getPhoneList() == cloneShallowPrototype.getPhoneList());
}
}
复制代码
由以上测试类可以看出,克隆前后两个对象的phoneList
属性是相等的,说明这两个属性指向的是同一个内存地址,所以其中的一个对象修改了这个属性,另一个也就会随之改变。
package cn.liangyy.prototype;
import java.util.ArrayList;
import java.util.List;
public class TestShallowPrototype2 {
public static void main(String[] args) {
//初始化一个原型实例对象ShallowPrototype
ShallowPrototype shallowPrototype = new ShallowPrototype();
shallowPrototype.setAge(20);
shallowPrototype.setName("亚索");
List<String> phoneList = new ArrayList<>();
phoneList.add("133xxxx452");
shallowPrototype.setPhoneList(phoneList);
//克隆原型对象
ShallowPrototype cloneShallowPrototype = (ShallowPrototype) shallowPrototype.clone();
//打印结果
System.out.println("克隆前:"+shallowPrototype.getPhoneList());
System.out.println("克隆后:"+cloneShallowPrototype.getPhoneList());
//比较两个对象是否指向同一内存地址
System.out.println(shallowPrototype.getPhoneList() == cloneShallowPrototype.getPhoneList());
//修改原对象中的属性phoneList
List<String> list = shallowPrototype.getPhoneList();
list.add("155xxxx1564");
//再次打印结果
System.out.println("克隆前:(修改后)"+shallowPrototype.getPhoneList());
System.out.println("克隆后:(修改后)"+cloneShallowPrototype.getPhoneList());
}
}
复制代码
通过这个测试可以看到,当我们对phoneList
进行了一个添加操作之后,两个对象都一同被修改了。
上面就是一个浅克隆的示例,浅克隆存在的问题使它有些时候并不适合我们的业务需求,下面我们看一下深克隆的示例。
package cn.liangyy.prototype.deep;
import java.io.*;
import java.util.List;
public class DeepPrototype implements Cloneable, Serializable {
private String name;
private int age;
private List<String> phoneList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<String> getPhoneList() {
return phoneList;
}
public void setPhoneList(List<String> phoneList) {
this.phoneList = phoneList;
}
public DeepPrototype deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
DeepPrototype clone = (DeepPrototype) ois.readObject();
return clone;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public Object clone(){
return this.deepClone();
}
}
复制代码
package cn.liangyy.prototype.deep;
import java.util.ArrayList;
import java.util.List;
public class TestDeepPrototype {
public static void main(String[] args) {
DeepPrototype deepPrototype = new DeepPrototype();
deepPrototype.setAge(20);
deepPrototype.setName("亚索");
List<String> phoneList = new ArrayList<>();
phoneList.add("132xxxx4562");
deepPrototype.setPhoneList(phoneList);
DeepPrototype cloneDeepPrototype = (DeepPrototype) deepPrototype.clone();
System.out.println("克隆前:"+deepPrototype.getPhoneList());
System.out.println("克隆后:"+cloneDeepPrototype.getPhoneList());
System.out.println(deepPrototype.getPhoneList() == cloneDeepPrototype.getPhoneList());
}
}
复制代码
由上面的测试,可以看到,虽然输出的值是相同的,但是最后的结果却是false
,这就说明原对象和克隆对象之间是完全独立,phoneList
属性没有共用同一个内存地址。
package cn.liangyy.prototype.deep;
import java.util.ArrayList;
import java.util.List;
public class TestDeepPrototype2 {
public static void main(String[] args) {
DeepPrototype deepPrototype = new DeepPrototype();
deepPrototype.setAge(20);
deepPrototype.setName("亚索");
List<String> phoneList = new ArrayList<>();
phoneList.add("132xxxx4562");
deepPrototype.setPhoneList(phoneList);
DeepPrototype cloneDeepPrototype = (DeepPrototype) deepPrototype.clone();
System.out.println("克隆前:"+deepPrototype.getPhoneList());
System.out.println("克隆后:"+cloneDeepPrototype.getPhoneList());
System.out.println(deepPrototype.getPhoneList() == cloneDeepPrototype.getPhoneList());
//修改原对象中的属性phoneList
List<String> list = deepPrototype.getPhoneList();
list.add("188xxxx7896");
System.out.println("克隆前:"+deepPrototype.getPhoneList());
System.out.println("克隆后:"+cloneDeepPrototype.getPhoneList());
}
}
复制代码
由该测试类,我们可以清楚的知道,修改了原对象的引用属性之后,并没有影响到克隆对象。
关于深克隆上面的示例,深克隆实现了两个接口:Cloneable
和Serializable
。为什么要实现这两个接口呢?
Serializable
接口大家应该比较好理解,因为我们的deepClone
方法是通过序列化来实现对象克隆的,所以必须要实现序列化接口,否则会抛出异常NotSerializableException
。
对于Cloneable
接口,因为clone
方法是Object
对象的,而 Java 中所有的对象默认都是Object
对象的子类,所以我们的类中也同样的具有clone
方法,但是默认的clone
方法实现的是浅克隆,为了使得我们的深克隆对象中不会同时具备深克隆和浅克隆两个功能,我这里选择了重写clone
方法,而 Java 中的规范约定,如果我们需要实现clone
对象,那么必须要实现Cloneable
接口,否则就会抛出异常CloneNotSupportedException
。
原型模式使用场景原型模式的作用就是克隆对象,而如果一个对象本身的创建就非常简单,那么没必要使用克隆模式,所以原型模式一般适用于类初始化消耗资源较多时或者就是创建一个对象非常复杂的场景。
原型模式的优点
原型模式的缺点
评论