什么是原型(Prototype)模式
- 原型模式是一个创建型模式
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象;即对象的拷贝;
优缺点
- 优点:在初始化信息不发生变化的情况下,使用克隆,即隐藏了对象的创建细节,对性能有提升;
- 缺点:每个类必须准备一个clone方法,且clone方法需要对类的功能进行考虑;
简单的UML图
原型模式的应用场景(相对来说)
- new 构造对象需要非常繁琐的数据准备时,可以使用原型模式,进行对象的快速复制;
- 多方使用当前对象,且都会对当前对象进行修改,调用方又相互独立,各自修改的值不想影响到其他调用方时,可以考虑通过原型模式复制多个对象(深拷贝)供调用方使用;
- 创建对象时,需要保留原型对象的数据时;
- 注意,通过实现Cloneable接口的原型模式在调用clone()方法并不一定都比new构造对象快,只有new构造对象较为耗时或成本较高时考虑使用;所以使用前需要仔细考虑,并进行相应的测试;
- 原型模式利用对象拷贝的形式实现,需要注意对象的浅拷贝、深拷贝的不同
浅拷贝
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
clone实现浅拷贝
public class ShallowClone implements Cloneable {
private boolean flag;
private Integer index;
private String str;
private ShallowPeople shallowPeople;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public ShallowPeople getShallowPeople() {
return shallowPeople;
}
public void setShallowPeople(ShallowPeople shallowPeople) {
this.shallowPeople = shallowPeople;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "ShallowClone{" +
"flag=" + flag +
", index=" + index +
", str='" + str + '\'' +
", shallowPeople=" + shallowPeople +
'}';
}
}
public class ShallowPeople {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
public class CloneMain {
private static final Log LOGGER = LogFactory.getLog(CloneMain.class);
public static void main(String [] args) throws Exception {
shallowClone();
System.out.println("-----------------------------------------");
depthClone();
System.out.println("-----------------------------------------");
depthCloneSerializable();
}
/**
* 浅拷贝测试
*/
private static void shallowClone() {
ShallowClone shallowClone = new ShallowClone();
shallowClone.setFlag(true);
shallowClone.setIndex(1);
ShallowPeople shallowPeople = new ShallowPeople();
shallowPeople.setAge(12);
shallowPeople.setName("xiaoming");
shallowClone.setShallowPeople(shallowPeople);
shallowClone.setStr("123");
System.out.println("原:" + shallowClone);
/**
* 这里可以看出,修改clone出来的对象里的ShallowPeople 信息,影响到了原对象
* 这是因为浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
*/
ShallowClone shallowClone1 = null;
try {
shallowClone1 = (ShallowClone) shallowClone.clone();
} catch (CloneNotSupportedException e) {
LOGGER.error("shallow clone error : {}", e);
return;
}
shallowClone1.getShallowPeople().setName("heihei");
shallowClone1.setStr("456");
System.out.println("新:" + shallowClone1);
System.out.println("原1:" + shallowClone);
}
/**
* 深拷贝测试
*/
private static void depthClone() {
DepthClone depthClone = new DepthClone();
depthClone.setFlag(false);
depthClone.setIndex(2);
DepthPeople depthPeople = new DepthPeople();
depthPeople.setAge(11);
depthPeople.setName("xiaoqiang");
depthClone.setDepthPeople(depthPeople);
System.out.println("原:" + depthClone);
/**
* 这里可以看出,修改clone出来的对象里的ShallowPeople 信息,对原对象无影响
* 这是因为深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
*
* 通过例子看出,深拷贝则是对浅拷贝的递归
*/
DepthClone depthClone1 = null;
try {
depthClone1 = (DepthClone) depthClone.clone();
} catch (CloneNotSupportedException e) {
LOGGER.error("depth clone error : {}", e);
return;
}
depthClone1.getDepthPeople().setName("heihei");
System.out.println("新:" + depthClone1);
System.out.println("原1:" + depthClone);
}
private static void depthCloneSerializable() {
DepthCloneSerializable depthCloneSerializable = new DepthCloneSerializable();
depthCloneSerializable.setFlag(true);
depthCloneSerializable.setIndex(3);
People people = new People();
people.setAge(20);
people.setName("xiaoliu");
depthCloneSerializable.setPeople(people);
System.out.println("原:" + depthCloneSerializable);
DepthCloneSerializable depthCloneSerializable1 = null;
try {
depthCloneSerializable1 = (DepthCloneSerializable) depthCloneSerializable.deepClone();
} catch (Exception e) {
System.err.println("depthCloneSerializable error : " + e.getMessage());
LOGGER.error("depthCloneSerializable error : {}", e);
return;
}
depthCloneSerializable1.getPeople().setName("heihei");
System.out.println("新:" + depthCloneSerializable1);
System.out.println("原1:" + depthCloneSerializable);
}
}
我们可以通过输出结果看出,浅拷贝生成的对象中的ShallowPeople信息修改,影响到了原型对象,原因是因为浅拷贝是仅拷贝对象本身,不拷贝对象内包含的引用指向的对象,且保留引用;
深拷贝
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象
clone实现深拷贝
public class DepthClone implements Cloneable {
private boolean flag;
private Integer index;
private DepthPeople depthPeople;
@Override
protected Object clone() throws CloneNotSupportedException {
DepthClone depthClone = null;
try {
depthClone = (DepthClone) super.clone();
depthClone.setDepthPeople((DepthPeople) depthClone.getDepthPeople().clone());
} catch (CloneNotSupportedException e) {
throw e;
}
return depthClone;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public DepthPeople getDepthPeople() {
return depthPeople;
}
public void setDepthPeople(DepthPeople depthPeople) {
this.depthPeople = depthPeople;
}
@Override
public String toString() {
return "DepthClone{" +
"flag=" + flag +
", index=" + index +
", depthPeople=" + depthPeople +
'}';
}
}
public class DepthPeople implements Cloneable {
private String name;
private Integer age;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "DepthPeople{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我们可以通过输出结果看出,深拷贝并没有对原型对象有任何影响;
通过以上实现可以看出,通过clone实现深拷贝,其实就是对原型对象的所有子引用对象进行再次clone;类似对浅拷贝的一种递归实现。
通过序列化的方式实现深拷贝
public class DepthCloneSerializable implements Serializable {
private boolean flag;
private Integer index;
private People people;
public Object deepClone() throws IOException, SecurityException, ClassNotFoundException {
// 写出去
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bo);
oos.writeObject(this);
// 读回来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bi);
return ois.readObject();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
public People getPeople() {
return people;
}
public void setPeople(People people) {
this.people = people;
}
@Override
public String toString() {
return "DepthCloneSerializable{" +
"flag=" + flag +
", index=" + index +
", people=" + people +
'}';
}
}
public class People implements Serializable {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "DepthPeople{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
拷贝结果
copyProperties工具及比较
引用:copyProperties工具及比较
引用:Orika介绍
Orkia