原型设计模式(Prototype Design Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需知道其具体类型。这种模式通常用于需要创建多个相似对象的情况,以避免重复的实例化过程,提高性能和代码复用性。
主要角色:
-
原型(Prototype):定义了一个用于复制现有对象以创建新对象的接口。
-
具体原型(Concrete Prototype):实现了原型接口的具体类,负责实现对象的复制操作。
-
客户端(Client):通过调用原型对象的克隆方法来创建新对象,而无需知道具体的实现细节。
工作原理:
-
首先,需要有一个原型对象作为模板,这个原型对象需要实现一个克隆方法(Clone方法)来复制自身。
-
客户端通过调用原型对象的克隆方法来复制对象,而不是通过直接实例化新对象。
-
当需要创建新对象时,客户端向原型对象发出克隆请求,原型对象将复制自身并返回一个新对象。
优点:
-
简化对象创建:避免了重复的实例化过程,提高了对象创建的效率。
-
灵活性:可以动态地添加或删除原型,以满足不同的需求。
-
保护现有对象:可以防止对现有对象的意外修改,因为复制的是一个新对象。
适用场景:
-
当需要创建的对象包含大量相似部分或需要频繁创建相似对象时。
-
当对象的创建过程比较复杂或需要消耗大量资源时,可以通过复制现有对象来节省资源。
举例:
1.创建一个具体原型
/**
* 具体原型
* 实现一个接口 Cloneable
* 重写一个方法 clone
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Video implements Cloneable{
private String name;
private Date createTime;
/*
浅克隆
*/
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.创建一个客户端
/*
客户端:克隆
*/
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象
Video v1 = new Video("Bilibili", new Date());
Video v2 = (Video) v1.clone();//浅克隆
v1.setName("张三");
System.out.println(v1);
System.out.println(v2);
System.out.println(v1 == v2);//false 克隆出来的对象,是一个新对象,只是内容相同
System.out.println(v1.getCreateTime()==v2.getCreateTime());//true 不过两个对象之中的引用还是指向同一个引用对象
System.out.println(v1.getName()==v2.getName());//false,String类型是不可变类型
}
}
在克隆方法中简单的使用super.clone()方法是浅克隆,对于克隆出来的新对象中的引用数据类型的变量实际指向的还是与旧对象中的对应的引用数据类型变量的同一个内存地址。
在Java中,
String
类型和Date
类型在某种程度上都可以看作是引用类型,但是它们的内部实现机制不同,导致了在进行克隆时表现出的不同行为。
String类型:
String
是不可变类,它的实例在创建后不可修改。当你对String
实例进行修改时,实际上是创建了一个新的String
实例。因此,使用clone()
方法克隆String
实例时,会生成一个新的String
实例,其值与原始实例相同,但是引用地址是不同的。Date类型:
Date
是可变类,它的实例是可以修改的。当你修改Date
实例的值时,实际上是修改了该实例的属性值,而不会创建新的实例。因此,使用clone()
方法克隆Date
实例时,会生成一个新的Date
实例,但是该实例的引用地址与原始实例相同,因此它们指向同一个对象。在上面的代码中,由于
clone()
方法是浅克隆,所以对于Date
类型的属性createTime
,克隆出来的对象与原始对象共享同一个Date
实例,因此它们的引用地址是相同的。而对于String
类型的属性name
,由于String
是不可变类,克隆出来的对象会生成一个新的String
实例,所以它们的引用地址是不同的。
如何将其修改为深克隆?将clone中的代码进行如下修改
/*
深克隆:将对象中的每一个引用类型的变量都进行克隆
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Video video = (Video) super.clone();
video.setCreateTime((Date) this.createTime.clone());
return video;
}
Date类型也实现了Cloneable接口,重写了clone方法。
可以看到,Date中的clone方法也是采用的我们所实现的深克隆方式。