原型模式(Prototype Pattern)定义:用一个已经建立的实例做为原型,经过复制该原型对象来建立一个和原型相同或类似的新对象java
在《英雄联盟》这款游戏里,有不少小兵。咱们定义小兵类:markdown
// Minion.java
public class Minion{
/**
* 小兵类型:近战小兵MeleeMinion、远程小兵CasterMinion、攻城小兵SiegeMinion、超级兵SuperMinion
*/
private String type;
/**
* 颜色
*/
private String color;
/**
* 血量
*/
private int hp;
/**
* 武器
*/
private Weapon weapon;
public Minion(String type, String color, int hp) {
System.out.println("开始构造小兵");
this.type = type;
this.color = color;
this.hp = hp;
}
}
复制代码
咱们建立一个小兵,只须要这样Minion minion = new Minion("Melee", "Blue", 200);
app
在每一波兵中,都有五个这样小兵,咱们就须要new 五个对象。若是这个构造方法是一个很是复杂或耗性能的操做,那咱们建立五个对象过程当中,就消耗大量系统资源。ide
在这种场景中,就很是适合原型模式:用一个已经建立的小兵做为原型,经过复制该小兵来建立和这个小兵相同或类似的其余小兵。函数
原型模式的核心就是经过复制现有的实例建立新的实例。在Java的Object类中,clone()方法为咱们提供了复制对象的功能。性能
protected native Object clone() throws CloneNotSupportedException;
复制代码
具体的实现方式,只须要在类中实现Cloneable接口(若类没有实现Cloneable接口,调用clone方法将抛出CloneNotSupportedException异常)。ui
// 小兵类,实现Cloneable接口
public class Minion implements Cloneable{
/**
* 小兵类型:近战小兵MeleeMinion、远程小兵CasterMinion、攻城小兵SiegeMinion、超级兵SuperMinion
*/
private String type;
/**
* 颜色
*/
private String color;
/**
* 血量
*/
private int hp;
public Minion(String type, String color, int hp) {
System.out.println("开始构造小兵");
this.type = type;
this.color = color;
this.hp = hp;
}
@Override
public Minion clone() {
try {
// 调用Object类clone方法便可
Minion copy = (Minion) super.clone();
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void display() {
System.out.println("我是" + type + "小兵" + ";颜色:" + color + ";血量:" + hp);
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public static void main(String[] args) throws Exception{
Minion minion = new Minion("Melee", "Blue", 200);
Minion minionCopy = minion.clone();
System.out.println("minion == minionCopy:" + (minion == minionCopy));
minionCopy.display();
}
}
// 输出结果:
开始构造小兵
minion == minionCopy:false
我是Melee小兵;颜色:Blue;血量:200
复制代码
从输出结果咱们能够看出:this
拷贝对象(clone),能够分为两种拷贝方式:lua
浅拷贝:拷贝对象的引用,而不是copy对象自己url
深拷贝:拷贝对象的自己,即会建立一个新的对象
java提供的clone方法,实现的是浅拷贝。咱们能够实际验证一下:
仍是刚才的Minion类,如今给小兵加一个武器Weapon
// 武器类
public class Weapon implements Cloneable{
/**
* 武器类型
*/
private String type;
/**
* 武器是否损坏
*/
private boolean isDamage;
public Weapon(String type) {
this.type = type;
isDamage = false;
}
/**
* 武器损坏
*/
public void destroy() {
this.isDamage = true;
}
/**
* 武器是否损坏
* @return
*/
public boolean isDamage() {
return isDamage;
}
}
// 小兵类
public class Minion implements Cloneable{
/**
* 武器
*/
private Weapon weapon;
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public static void main(String[] args) throws Exception{
Minion minion = new Minion("Melee", "Blue", 200);
Weapon weapon = new Weapon("刀");
minion.setWeapon(weapon);
Minion minionCopy = minion.clone();
System.out.println("复制小兵1武器是否损坏:" + minionCopy.getWeapon().isDamage());
// 原始小兵武器损坏
minion.getWeapon().destroy();
System.out.println("复制小兵1武器是否损坏:" + minionCopy.getWeapon().isDamage());
}
}
// 输出结果
开始构造小兵
复制小兵1武器是否损坏:false
复制小兵1武器是否损坏:true
复制代码
在main方法中代码中,咱们只让原始小兵武器损坏,但从输出结果来看,咱们复制小兵的武器也坏了。这就印证了咱们上面的结论:java提供的clone方法,实现的是浅拷贝。
其实很简单,咱们能够手动建立须要深拷贝的对象
public Minion clone() {
try {
Minion copy = (Minion) super.clone();
// 手动建立对象
Weapon weapon = new Weapon(this.getWeapon().getType());
copy.setWeapon(weapon);
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
复制代码
这种手动建立的方式违背了咱们克隆对象的初衷,咱们能够改为:对须要拷贝的成员对象再次克隆
// 武器对象实现Cloneable接口
public class Weapon implements Cloneable{
@Override
public Weapon clone() {
try {
return (Weapon) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
// 小兵类的clone方法
@Override
public Minion clone() {
try {
Minion copy = (Minion) super.clone();
// 克隆对象
Weapon weapon = this.weapon.clone();
copy.setWeapon(weapon);
return copy;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
复制代码
这种克隆对象方式,有时实现起来会特别复杂,好比有大量的非基本数据类型的成员对象;成员对象的引用嵌套很是深,须要对每层都实现clone方法。
更通用的实现深拷贝的方式:经过序列化和反序列化的方式实现对象的拷贝
Java序列化是指将Java对象转为字节序列的过程,反序列化为把字节序列恢复为Java对象的过程。
具体代码:
@Override
public Minion clone() {
// 序列化、反序列化方式
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
return (Minion) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
复制代码
!!! 将要序列化的对象及其成员变量必定要实现Serializable接口,不然序列化时将抛出ava.io.NotSerializableException异常。