定义
在软件系统中有时候需要多次创建某一类型的对象,为了简化创建过程,可以只需要创建一个对象,然后再通过克隆的方式复制出多个相同的对象,这就是原型模式的设计思想。
因为是使用克隆获取多个对象,因此原型模式可以分为两种形式:浅克隆、深克隆。
- 浅克隆:对于基本数据类型,会进行数据的复制。而对于引用类型,只会复制其地址。因此,如果A是B浅克隆的结果,那么当我们修改A或B其中一方基本数据类型的值,另一方不会受到影响;但是如果我们修改其中一方的引用类型数据,另一方也会跟着一起改变,因为变量存储的只是一个地址值,它们指向相同的数据空间。
- 深克隆:对于基本数据类型,进行数据的复制。而对于引用类型,会重新开辟一个空间,再将引用类型的数据进行复制,引用类型变量指向的是一个新的地址。因此,如果A是B深克隆的结果,那么无论修改A或B其中一方的变量,另一方都不会受到影响。
浅克隆
浅克隆的实现方式:
- 实现
Cloneable
接口 - 重写
clone()
方法 - 调用
super.clone()
代码如下:
public class Class implements Cloneable {
int number;
ArrayList<String> students;
@Override
protected Object clone(){
Object object=null;
try {
object=super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return object;
}
}
测试代码:
public static void main(String[] args) {
Class A=new Class();
A.setNumber(50);
ArrayList<String> listA=new ArrayList<>();
listA.add("A");
A.setStudents(listA);
Class B=(Class)A.clone();
System.out.println("A:"+A);
System.out.println("B:"+B);
System.out.println("change");
B.setNumber(44);
ArrayList<String> listB = B.getStudents();
listB.set(0,"B");
System.out.println("A:"+A);
System.out.println("B:"+B);
}
输出结果:修改基本数据类型的数据,另一方不会跟着改变。修改引用类型数据,同时改变。
深克隆
深克隆有两个实现方法:
- 利用序列化:先将对象序列化转换成字节序列,再反序列化将字节序列恢复为对象。
- 利用引用类型自身实现的clone()方法。
深克隆方式一:序列化方法
- 实现序列化接口
Serializable
- 序列化:将对象写入流中
- 反序列化:将对象从流中取出
代码如下:
public class Class implements Serializable {
int number;
ArrayList<String> students;
Object deepClone1() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this); //写入流
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ois.readObject()); //从流读出
}
}
测试:由于在流操作时将异常抛出了,所以在调用时要处理异常。
public static void main(String[] args) {
Class A=new Class();
A.setNumber(50);
ArrayList<String> listA=new ArrayList<>();
listA.add("A");
A.setStudents(listA);
//Class B=(Class)A.clone();
Class B= null;
try {
B = (Class)A.deepClone1();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("A:"+A);
System.out.println("B:"+B);
System.out.println("change");
B.setNumber(44);
ArrayList<String> listB = B.getStudents();
listB.set(0,"B");
System.out.println("A:"+A);
System.out.println("B:"+B);
}
输出结果:修改一方的基本类型和引用类型,另一方都不会改变。
深克隆方式二:利用引用类型自身的Clone()方法
- 实现
Cloneable
接口 - 先
super.clone()
克隆整个对象。 - 对于引用类型,调用其自身的clone()方法。
代码如下:
public class Class implements Cloneable,Serializable {
int number;
ArrayList<String> students;
Class deepClone2(){
Class a=null;
try {
a=(Class)super.clone();
a.students=(ArrayList)this.students.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return a;
}
}
为什么这样使用就可以实现深克隆呢?我们可以查看一下ArrayList中clone()的源码,如下:
public Object clone() {
try {
ArrayList var1 = (ArrayList)super.clone();
//Arrays.copyOf方法会重新开辟一个this.size的空间,然后将this.elementData的数据复制到新的空间
var1.elementData = Arrays.copyOf(this.elementData, this.size);
var1.modCount = 0;
return var1; //返回的var1是新的地址,而不是原先的地址值
} catch (CloneNotSupportedException var2) {
throw new InternalError(var2);
}
}
测试代码:
public static void main(String[] args) {
Class A=new Class();
A.setNumber(50);
ArrayList<String> listA=new ArrayList<>();
listA.add("A");
A.setStudents(listA);
Class B=A.deepClone2();
System.out.println("A:"+A);
System.out.println("B:"+B);
System.out.println("change");
B.setNumber(44);
ArrayList<String> listB = B.getStudents();
listB.set(0,"B");
System.out.println("A:"+A);
System.out.println("B:"+B);
}
输出结果:修改一方的基本类型和引用类型,另一方都不会改变。
结论
使用原型模式,可以利用已有的实例简化对象的创建过程;如果需要保存变化不大的对象的状态,可以用深克隆方式进行备份。但需要为每一类配备一个克隆方法,如果是已存在的类,则会违背开闭原则。