原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象,就是以某个东西为原型,将它进行拷贝。(也就是复制粘贴的思想)
目录
原型模式的定义(Prototype Pattern)
模式动机
1、复制一个对象,从而克隆出多个与原型对象一模一样的对象——原型模式
2、有些对象的创建过程较为复杂,而且需要频繁创建
3、通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象
与new的区别:克隆有cloneable接口和clone()方法,new相当于创造了一个新的对象,会有原本的默认值,而克隆是直接拷贝了一个对象,完全一致。
原型模式的适用场景
在以下情况下可以使用原型模式:
1、创建新对象成本较大,新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改(或者是对象比较复杂的情况)
2、系统要保存对象的状态,而对象的状态变化很小
3、需要避免使用分层次的工厂类来创建分层次的对象
浅拷贝和深拷贝
浅拷贝:对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象);但如果被克隆的对象中包含除8中数据类型和String类型外的其他类型的属性,浅拷贝并不会克隆这些属性(即不会为这些属性分配内存,而是引用原来对象中的属性)。
深拷贝:深拷贝是在浅拷贝的基础上,递归地克隆除8种基本数据类型和String类型外的属性(即为这些属性重新分配内存而非引用原来对象中的属性)
深拷贝简单的理解就是在浅拷贝的基础上,进一步对对象属性进行依次拷贝,对象里面的复杂属性也重新生成了新的引用。
浅拷贝中由于除8种数据类型和String类型外的其他类型的属性不会被克隆,因此当通过新对象对这些属性进行修改时,原对象的属性也会同时改变。而深拷贝则已经对这些属性重新分配内存,所以当通过新对象对这些属性进行修改时,原对象的属性不会改变。
如图:
总结:
浅拷贝:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深拷贝:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
深拷贝实现方式:
序列化和反序列化就可以实现(效率低一点);改造clone()方法也可以实现(违背开闭原则);
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
实现步骤(浅拷贝)
简单的两步:实现一个接口Cloneable,重写一个方法clone()
这里以克隆视频为例
import java.util.Date;
public class Video implements Cloneable {
private String name;
private Date createTime;
//clone是本地方法(native)
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
import java.util.Date;
//客户端克隆
public class ClientCopy {
public static void main(String[] args) throws CloneNotSupportedException {
//创建原型对象 v1
Date date = new Date();
Video v1 = new Video("宋浩讲高数",date);
System.out.println("v1="+v1.getName());
System.out.println("v1="+v1.getCreateTime());
System.out.println("v1=>hash="+v1.hashCode());
//v2克隆v1
Video v2 = (Video)v1.clone();
System.out.println("v2="+v2.getName());
System.out.println("v2="+v2.getCreateTime());
System.out.println("v2=>hash="+v2.hashCode());
}
}
这里的hash值不一样说明它们是两个对象,在内存中的存储位置不同,hash值的计算是通过对象在内存中的逻辑地址进行加工变换的。
date的一个复杂属性,如果改变其中一个的date,那么另外一个的date也会改变
import java.util.Date;
//客户端克隆
public class ClientCopy {
public static void main(String[] args) throws CloneNotSupportedException {
//创建原型对象 v1
Date date = new Date();
Video v1 = new Video("宋浩讲高数",date);
System.out.println("v1="+v1.getName());
System.out.println("v1="+v1.getCreateTime());
System.out.println("v1=>hash="+v1.hashCode());
//v2克隆v1
Video v2 = (Video)v1.clone();
System.out.println("v2="+v2.getName());
System.out.println("v2="+v2.getCreateTime());
System.out.println("v2=>hash="+v2.hashCode());
//改变v1的name和date属性:
System.out.println("-------------------------------");
v1.setName("mouse");
date.setTime(2332322);
System.out.println("v1="+v1.getName());
System.out.println("v1="+v1.getCreateTime());
System.out.println("v1=>hash="+v1.hashCode());
System.out.println("v2="+v2.getName());
System.out.println("v2="+v2.getCreateTime());
System.out.println("v2=>hash="+v2.hashCode());
}
}
可以看到,v2的name属性没有改变,但是date改变了。这就是浅拷贝
实现步骤(深拷贝)
这里以改造clone()方法为例:
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Video v = (Video)obj;
//将对象的属性也进行克隆(实现深克隆)
v.createTime = (Date)this.createTime.clone();
return obj;
}
原型模式的应用
Spring的bean的创建就应用了原型模式和单例模式
原型模式也会跟工厂模式打交道,之前工厂模式是new,现在可以使用拷贝
原型模式的优缺点
优点:
1、简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
2、扩展性较好
3、简化创建结构,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品
4、可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作
缺点:
1、需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则
2、在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦