文章目录
一、简介
1.1 定义
定义:原型(Prototype)模式属于创建者模式。通过创建一个实例作为原型,并且通过复制这些原型来创建新的对象。
- 原型对象需要提供一个方法(实现clone()方法),让该原型对象调用此方法可以复制一个和自己有相同状态的同类型对象
- 原型对象和克隆出的新对象可以分别独立的变化。比如:我们复制一个word文档中的文本,然后创建一个新的word文档,我们可以对两个文档中的内容进行修改且互不影响。
1.2 clone方法和Cloneable接口
clone()方法
- clone()方法用来创建并返回此对象的拷贝,是Object类中的一个protected权限的方法,因为Object是超级父类,因此所有的类都继承了clone()方法,但在其他类中不能直接调用clone()方法
- 若要调用clone()方法,该类必须实现Cloneable接口。如果没有实现Cloneable接口的类的实例调用了clone()方法,则会在运行时抛出CloneNotSupportedException异常。
Cloneable接口
Cloneable接口中没有任何方法,该接口的唯一作用就是表示实现该接口的对象可以被克隆
public class A{
public static void main(String[] args) throws CloneNotSupportedException {
B obj1 = new B();
B obj2 = (B) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
//具体原型类
class B implements Cloneable {
B() {
System.out.println("创建成功实例B!");
}
public Object clone() throws CloneNotSupportedException {
System.out.println("原型B复制成功!");
return (B) super.clone();
}
}
//执行结果
创建成功实例B!
原型B复制成功!
obj1==obj2?false
1.3 浅拷贝和深拷贝
浅拷贝(浅复制、浅克隆)
Java中的数据类型分为基本数据类型和引用数据类型。
- 基本数据类型:浅拷贝会将所有的基本数据类型和String类型的数据复制一份到新的对象中去
- 引用数据类型:对于引用型变量,浅拷贝只会复制该对象的引用(内存地址),并不会复制这个引用型变量所指向的对象。
深拷贝(深复制、深克隆)
深拷贝和浅拷贝的不同在于:深拷贝不仅会复制对象的引用,还会复制引用所指向的对象
1.4 利用序列化实现深拷贝
序列化和反序列化
- 把对象写到流里的过程是序列化过程;
- 把对象从流中读出来的过程则叫反序列化过程。
- 在Java语言里深度克隆一个对象,先使对象实现Serializable接口,然后把对象(实际上只是对象的拷贝)写到一个流里(序列化),再将对象从流里读回来(反序列化)
public Object deepClone() throws IOException, ClassNotFoundException{
//将对象写到流里
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//从流里读回来
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
二、模式原理
2.1 模式组成
组成(角色) | 作用 |
---|---|
Prototype(抽象原型) | 一个接口,负责定义对象复制自己的方法,可以替换为Cloneable接口 |
ConcretePrototype(具体原型) | 实现Prototype接口的类,实现克隆自己的操作 |
Client(客户类) | 让一个原型对象克隆自己,创建一个新的对象 |
2.2 UML类图
三、实例
通过原型模式来克隆一把AWM和一辆玛莎拉蒂
3.1 实例代码
- 创建具体原型类:创建AWM和Maserati原型
//AWM原型
public class AWM implements Cloneable {
// 颜色和子弹数
public String color;
public int ziDan;
public AWM(String color,int ziDan) {
this.color = color;
this.ziDan = ziDan;
}
@Override
public String toString() {
return "AWM [color=" + color + ", ziDan=" + ziDan + "]";
}
public Object clone() {
AWM awm = null;
try {
// 调用Object的clone方法,然后强制转换为Maserati类型
awm = (AWM) super.clone();
} catch (Exception e) {
System.out.println(e);
}
return awm;
}
}
//玛莎拉蒂原型
public class Maserati implements Cloneable {
//颜色和耗损
public String color;
public int hp;
public Maserati(String color,int hp){
this.color = color;
this.hp = hp;
}
@Override
public String toString() {
return "Maserati [color=" + color + ", hp=" + hp + "]";
}
public Object clone() {
Maserati maserati = null;
try {
// 调用Object的clone方法,然后强制转换为Maserati类型
maserati = (Maserati)super.clone();
} catch (Exception e) {
System.out.println(e);
}
return maserati;
}
}
- 利用原型模式复制AWM和Maserati
public class Client {
public static void main(String[] args) {
//创建一把AWM:输入颜色和子弹
AWM awm = new AWM("black",20);
//复制一把awm
AWM awm2 = (AWM)awm.clone();
System.out.println(awm.toString()+" awm.hashCode="+awm.hashCode());
System.out.println(awm2.toString()+" awm2.hashCode="+awm2.hashCode());
System.out.println(awm == awm2);
//创建一辆玛莎拉蒂:输入颜色和hp
Maserati maserati = new Maserati("red",100);
//复制一辆玛莎拉蒂
Maserati maserati2 = (Maserati)maserati.clone();
System.out.println(maserati.toString()+" maserati.hashCode="+maserati.hashCode());
System.out.println(maserati2.toString()+" maserati2.hashCode="+maserati2.hashCode());
System.out.println(maserati == maserati2);
}
}
//执行结果
AWM [color=black, ziDan=20] awm.hashCode=1284693
AWM [color=black, ziDan=20] awm2.hashCode=31168322
false
Maserati [color=red, hp=100] maserati.hashCode=17225372
Maserati [color=red, hp=100] maserati2.hashCode=5433634
false
3.2 UML类图
四、优缺点
4.1 优点
- 当创建新实例的代价更大时,使用原型模式复制一个已有的实例可以提高创建新实例的效率
- 利用clone()方法创建对象要比使用new创建对象快
- 可以在不修改其他代码的情况下添加新的产品
4.2 缺点
- 每一个类都需要有一个clone方法
- clone()方法在类的内部,当对已有的类进行修改时,需要修改源代码,违背了开闭原则
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦