原型模式
1.经典案例
- 克隆羊多利。有姓名,身高,体重,年龄等属性。可以用来当作模板,克隆10只属性完全相同的羊。
- 三好学生奖状。有姓名,学校,获奖时间等属性。奖状可以当作模板,打印多份,使用时只需要修改姓名,学校,获奖时间。
常规解法
1.定义sheep实体类,生成构造方法、getter和setter方法、toString方法
2.实例化10个对象
缺点:
效率低,每次创建对象都需要获取原始对象的属性,不能动态获取对象运行时的状态。
新思路-原型模式
2.原型模式定义
【创建型】设计模式。允许另外一个对象再创建另外一个可定制的对象
用原型实例指定创建的对象,并通过拷贝原型,创建新对象,同时保证性能
3.工作原理
- 原型类声明克隆自己的接口
- Client类让具体的原型类对象克隆自己,创建一个新的对象
- Prototype:声明一个克隆自己的接口
- client:客户端让一个原型对象克隆自己
- ConcretePrototype:实现一个克隆自己的操作
4.原型模式分类
- 浅拷贝
代码思路:
在实体类的基础上实现Cloneable接口,重写clone方法
Demo中调用克隆对象
代码演示:
🐏的实体类
package com.henji.clone;
/**
* Sheep类基础上实现Cloneable接口,重写clone方法。
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep implements Cloneable {
private String name;
private int age;
@Override
protected Object clone () throws CloneNotSupportedException {
Sheep sheep = null;
sheep = (Sheep) super.clone ();
return sheep;
}
/*******省略构造方法、setter、getter、toString方法*******/
}
🐏的实现类
package com.henji.clone;
/**
* @author henji
* @date 2023/3/6 20:06
*/
public class Demo {
public static void main (String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep ("喜羊羊", 2);
Object c1 = sheep.clone ();
Object c2 = sheep.clone ();
Object c3 = sheep.clone ();
System.out.println (c1 + "===" + c1.hashCode ());
System.out.println (c2 + "===" + c2.hashCode ());
System.out.println (c3 + "===" + c3.hashCode ());
}
}
运行结果
总结:
- 三个对象的哈希值不同,说明拷贝进行是值传递,而不是引用传递
继续往下看:
此时慢羊羊也来凑热闹了,再新建一个🐏的实体类,并再Sheep类添加一个引用属性。再拷贝一遍
新建的慢羊羊实体类
package com.henji.clone;
/**
* 慢羊羊的实体类
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep2 {
private String name;
private int age;
/*******省略构造方法、setter、getter、toString方法*******/
}
添加了慢羊羊的引用属性的🐏实体类
package com.henji.clone;
/**
* Sheep类基础上实现Cloneable接口,重写clone方法。
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep implements Cloneable {
private String name;
private int age;
public Sheep2 sheep2;//引用慢羊羊
@Override
protected Object clone () throws CloneNotSupportedException {
Sheep sheep = null;
sheep = (Sheep) super.clone ();
return sheep;
}
/*******省略构造方法、setter、getter、toString方法*******/
}
实例化慢羊羊
package com.henji.clone;
/**
* @author henji
* @date 2023/3/6 20:06
*/
public class Demo {
public static void main (String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep ("喜羊羊", 2);
sheep.sheep2 = new Sheep2 ("慢羊羊", 8);
Sheep c1 = (Sheep) sheep.clone ();
Sheep c2 = (Sheep) sheep.clone ();
Sheep c3 = (Sheep) sheep.clone ();
System.out.println (c1 + "===" + c1.hashCode () + ":::" + c1.sheep2 + "===" + c1.sheep2.hashCode ());
System.out.println (c2 + "===" + c2.hashCode () + ":::" + c2.sheep2 + "===" + c2.sheep2.hashCode ());
System.out.println (c3 + "===" + c3.hashCode () + ":::" + c3.sheep2 + "===" + c3.sheep2.hashCode ());
}
}
运行结果
总结:
- 拷贝的三个喜羊羊对象哈希地址各不相同,依旧发生了值传递,但三个慢羊羊的地址完全相同,没有发生拷贝动作,地址指向克隆前的同一个对象。(如何让慢羊羊的拷贝对象也发生值传递?)
特征:
- 浅拷贝使用默认的clone()方法实现
- 基本数据类型的成员变量,浅拷贝会直接进行值传递
- 引用数据类型的成员变量,浅拷贝会进行引用传递
- 深拷贝
代码思路:
- 慢羊羊的实体类也实现Cloneable接口
- 实体类实现序列化接口,不实现Cloneable接口
实现Cloneable接口,重写clone
/**
* 慢羊羊的实体类
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep2 implements Cloneable {
private String name;
private int age;
/*
使用默认clone方法
*/
@Override
protected Object clone () throws CloneNotSupportedException {
return super.clone ();
}
}
在喜羊羊的实体类clone方法中调用慢羊羊的clone方法
package com.henji.clone;
/**
* Sheep类基础上实现Cloneable接口,重写clone方法。
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep implements Cloneable {
private String name;
private int age;
public Sheep2 sheep2;
@Override
protected Object clone () throws CloneNotSupportedException {
Object deep = null;
//对基本数据类型拷贝
deep = super.clone ();
//对引用数据类型拷贝
Sheep sheep = (Sheep) deep;
sheep.sheep2 = (Sheep2) sheep2.clone ();
return sheep;
}
}
再次运行Demo类
运行结果:
总结:
- 两个类(基本数据类型,引用数据类型)的拷贝,都实现了值传递,达成了深拷贝。
特征:
- 基本数据类型和引用数据类型,深拷贝都会对其进行值传递【复制】
通过序列化实现深拷贝
慢羊羊的实体类实现序列化接口,不需要再实现Cloneable接口package com.henji.clone;
import java.io.Serializable;
/**
* 慢羊羊的实体类
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep2 implements Serializable {
private String name;
private int age;
}喜羊羊的实体类也实现序列化接口,并使用流package com.henji.clone;
import java.io.*;
/**
* Sheep类基础上实现Cloneable接口,重写clone方法。
*
* @author henji
* @date 2023/3/6 19:08
*/
public class Sheep implements Serializable {
private String name;
private int age;
public Sheep2 sheep2;
/**
* 深拷贝
*
* @return
*/
Object deepClone () {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream ();
oos = new ObjectOutputStream (bos);
oos.writeObject (this);//输出当前对象
//反序列化
bis = new ByteArrayInputStream (bos.toByteArray ());
ois = new ObjectInputStream (bis);
Sheep sheep = (Sheep) ois.readObject ();
return sheep;
} catch (IOException e) {
throw new RuntimeException (e);
} catch (ClassNotFoundException e) {
throw new RuntimeException (e);
} finally {
try {
bos.close ();
oos.close ();
bis.close ();
ois.close ();
} catch (IOException e) {
throw new RuntimeException (e);
}
}
}
/*******省略构造方法、setter、getter、toString方法*******/客户端调用package com.henji.clone;
/**
* @author henji
* @date 2023/3/6 20:06
*/
public class Demo {
public static void main (String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep ("喜羊羊", 2);
sheep.sheep2 = new Sheep2 ("慢羊羊", 8);
Sheep c1 = (Sheep) sheep.deepClone ();
Sheep c2 = (Sheep) sheep.deepClone ();
Sheep c3 = (Sheep) sheep.deepClone ();
System.out.println (c1 + "===" + c1.hashCode () + ":::" + c1.sheep2 + "===" + c1.sheep2.hashCode ());
System.out.println (c2 + "===" + c2.hashCode () + ":::" + c2.sheep2 + "===" + c2.sheep2.hashCode ());
System.out.println (c3 + "===" + c3.hashCode () + ":::" + c3.sheep2 + "===" + c3.sheep2.hashCode ());
}
}
- 深拷贝和浅拷贝的区别
在于对引用数据类型的成员变量的拷贝
- 浅拷贝对引用数据类型进行引用传递
- 深拷贝对引用数据类型进行值传递
5.特点:
优点
- 动态获取对象运行时的状态,不需要重新初始化对象
- 原始对象发生增加或者减少属性,其他克隆对象也会发生变化,无须修改代码
- 创建的新对象比较复杂时,可以使用原型模式简化对象创建过程,提高效率
缺点
- 需要为每一个类重写一个克隆方法
- 对已存在的类进行改造需要修改源码,违背了开闭原则
- 对象存在多层引用,每一层对象都必须支持深拷贝,并且需要编写复杂的代码
6.应用场景
设计模式之原型设计应用-制作奖状