原型设计模式
原型模式
视频来源 原型模式 46-49
概述
原型模式(Prototype Pattern):使用原型模式指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建模式。
需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
角色
原型模式包含如下角色:
- 抽象原型类:规定了具体原型对象必须实现的clone方法,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
- 具体原型类:它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
- 访问类:使用具体原型类中的clone方法来复制新的对象。
Java语言提供了clone方法
所有的Java类都继承自 java.lang.Object,Object 类提供一个 clone() 方法,可以将一个Java对象复制一份。因此在Java中可以直接使用 Object 提供的 clone() 方法来实现对象的克隆,Java语言中的原型模式实现很简单。
需要注意的是能够实现克隆的Java类必须实现一个 标识接口 Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个 CloneNotSupportedException 异常。
实现
原型模式的克隆分为浅克隆和深克隆。
浅克隆: 创建一个新对象,新对象的属性和原对象完全相同,对于非基本类型属性,仍指向原有属性指向的对象的内存地址。
深克隆创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅克隆有点像 快捷方式 , 深克隆有点像复制文件
具体原型类:
public class Realizetype implements Cloneable {
public Realizetype() {
System.out.println("具体的原型对象创建完成!");
}
@Override
protected Realizetype clone() throws CloneNotSupportedException {
Realizetype realizetype = null;
realizetype = (Realizetype)super.clone()
System.out.println("具体原型复制成功!");
return realizetype;
}
}
客户端测试访问类
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype r1 = new Realizetype(); // 原型对象
Realizetype r2 = r1.clone(); // 克隆出来的对象
System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));
/**
* 具体的原型对象创建完成!
* 具体原型复制成功!
* 对象r1和r2是同一个对象?false
*/
}
}
运行结果:clone 原型对象的时候,不会调用构造方法,因为该对象不是通过 new 创建的
查看Object 中的clone方法
protected native Object clone() throws CloneNotSupportedException;
案例
【例】使用原型模式生成 “三好学生” 奖状。
代码如下:
/**
* 奖状类
*/
@Data
public class Citation implements Cloneable {
private String name; // 奖状上的名字
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println(name + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
/**
* 测出访问类
*/
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
Citation c1 = new Citation();
c1.setName("张三");
// 复制奖状
Citation c2 = c1.clone();
c2.setName("李四"); // 将奖状的名字改为李四
c1.show();
c2.show();
}
}
使用场景
以下两种情况,可以使用原型模式快捷的创建对象:
- 对象的创建非常复杂
- 性能和安全要求比较高。
深克隆
引用对象的浅克隆
将上面的 “三好学生” 奖状的案例中 Citation 类的 name 属性修改为 Student 类型的属性。
/**
* 学生类
*/
@Data
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
}
/**
* 奖状类
*/
@Data
public class Citation implements Cloneable {
private Student stu; // 这是个引用对象
void show() {
System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
public class CitationTest {
public static void main(String[] args) throws Exception {
// 原型对象
Citation c1 = new Citation();
Student stu1 = new Student("张三");
c1.setStu(stu1);
// 复制奖状并修改名字
Citation c2 = c1.deepClone();
Student stu2 = c2.getStu();
stu2.setName("李四");
// 判断stu1对象和stu2对象是否是同一个对象
System.out.println("st1和stu2是否同一个对象?" + (stu1 == stu2));
c1.show();
c2.show();
}
}
运行结果:stu1 和 stu2 是同一个对象
st1和stu2是否同一个对象?true
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
stu1 和 stu2 是同一个对象,就会产生将 stu1 中 name 属性值改为 “李四”,两个 Citation 对象中显示的都是李四。
这就是浅克隆的效果,对具体原型类(Citation)中的引用类型的属性进行引用的复制。
这种情况需要使用深克隆,而进行深克隆需要使用对象流。
深克隆实现1.文件流 + 对象流
学生类(Student)同上,但是要实现 Serializable 接口。
奖状类(Citation)实现 Serializable 接口,定义一个 deepClone 方法,通过文件输出流流和对象流实现深克隆。
// 1 所有对象都实现序列化的接口
// 2 自定义一个深度克隆方法deepClone, 通过文件流和对象流的方式实现对象的深度拷贝
public Citation deepClone() throws Exception {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));) {
oos.writeObject(this);
Citation citation = (Citation) ois.readObject();
return citation;
}
}
实现 2:字节数组流 + 对象流
学生类(Student)同上,实现 Serializable 接口。
奖状类(Citation)实现 Serializable 接口,定义一个 deepClone 方法,通过字节数组流和对象流实现深克隆。
// 1 所有对象都实现序列化的接口
// 2 自定义一个深度克隆方法deepClone, 通过字节数组流和对象流的方式实现对象的深度拷贝
public Citation deepClone2() throws Exception {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Citation) ois.readObject();
}
}
实现3:具体原型类中的引用类型进行克隆
public class Student implements Cloneable{
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//引用类型进行clone
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@Override
public Citation clone() throws CloneNotSupportedException {
Citation citation = (Citation) super.clone();
citation.setStu((Student)stu.clone());
return citation;
}
运行结果:
st1和stu2是否同一个对象?false
张三同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
李四同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!
原型模式在Spring中的应用场景
在Spring中,用户也可以采用原型模式来创建新的Bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
这里的原型模式,也就是常说的Spring中的多实例模式,Spring中还有大家熟知的单实例模式,即Sigleton
参考链接:https://blog.csdn.net/m0_53157173/article/details/120015362
参考链接:https://blog.csdn.net/weixin_43734095/article/details/122339858