一、原型模式:
- 概述:
创建型模式之一,它通过复制一个已有对象来获取更多相同或相似的对象,可提高对象创建效率,简化创建过程。
- 原理:
将一个原型对象传给要发动创建的对象(如客户端对象),这个客户端对象通过请求原型对象复制自己来实现创建过程。
二、原型模式结构:
- Prototype:抽象原型类
- ConcretePrototype:具体原型类
- Client:客户端类
三、浅克隆与深克隆:
关注成员变量是值类型还是引用类型,在深浅克隆中是如何复制这些变量。
- 浅克隆:
原型对象的成员变量是值类型,则将复制一份给克隆对象。但如果是成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的引用类型变量指向同一个内存地址。
- 深克隆:
无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。也就是说,除了对象本身被复制外,对象所有的成员变量也将被复制。
四、实现:
在Java语言中,所有的Java类均继承自Java.lang.Object类,Object类提供了一个clone()方法,可以用来实现对象的浅克隆。
- 派生类实现Cloneable接口,在派生类中覆盖基类的clone方法,并声明为public。
- 派生类的clone()方法中调用super.clone()。
此时,Object类相当于抽象原型类,所有实现了Cloneable接口的类相当于具体实现类。
注:
(1)克隆对象与原型对象不是同一个对象
(2)克隆对象与原型对象的类型一样
(3)如果对象x的equals方法定义恰当,那么x.clone().equals(x)应该成立。
Java深克隆可通过序列化的方式来实现。
序列化就是将对象写到流的过程,写到流中的对象是原有对象的一个复制,而原对象仍然在内存中。通过序列化实现的复制,不仅可以复制对象本身,而且可以复制其引用的成员对象。因此,通过序列化将对象写到一个流中,再从流中将其读取出来,可以实现深克隆。
- 实现Serializable接口。
- 将对象写入流中,将对象从流中读取。
五、应用案例:
分析:复制新的周报,每个周报对象不相同,但里面的附件需一样。即实现周报的浅克隆。
- 创建附件类Attachment。
public class Attachment {
private String name;
//getter,setter方法省略
public void download(){
System.out.println("下载附加,文件名为:"+name);
}
}
- 创建周报类WeeklyLog,包含附件类,实现Cloneable。
package Prototype.demo1;
public class WeeklyLog implements Cloneable{
//引用对象类
private Attachment attachment;
private String name;
private String date;
private String content;
//getter,setter方法省略
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 客户端client。
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
WeeklyLog log_pre,log_new;
log_pre=new WeeklyLog();
Attachment attachment=new Attachment();
log_pre.setAttachment(attachment);
//Java自带克隆机制
log_new= (WeeklyLog) log_pre.clone();
//判断是否实现了浅克隆
System.out.println("周报是否相同:"+(log_pre==log_new));
System.out.println("附件是否相同:"+(log_pre.getAttachment()==log_new.getAttachment()));
}
}
- 测试运行:
分析:如目前需要将附件也一同拷贝到周报中,可使用序列化,对周报对象进行深拷贝。此时,WeeklyLog类不再使用Java自带的克隆机制,而是通过序列化从头实现对象的深克隆。
- 附件类,周报类实现Serializable接口。
- 周报类编写深克隆方法。
//采用序列化技术实现深克隆
public WeeklyLog deepClone() 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 (WeeklyLog)ois.readObject();
}
- 测试运行:
六、总结:
(1)通过复制一个已有的对象来来获取更多相同或者相似的对象。
(2)浅克隆可以使用Java自带的克隆方法,深克隆可以通过实现序列化接口,将对象写入流中,再从流中进行读取的方法实现。
(3)浅克隆:只复制值类型变量,引用类型不复制,只复制其内存地址。深克隆:值类型,引用类型变量都复制。
(4)适用情况:创建新对象的成本大(例如初始化时间长,占用CPU资源等);系统要保存对象的状态,而对象的状态变化很小;