原型模式属于对象的创建模式。通过给出一个原型对象来指明所创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。相比于new出来的对象,原型模式效率高,因为原型直接复制的是对象的二进制,不是先new 对象再赋值。
原型模式的复制分为浅复制和深复制。浅复制中,对象的基本数据类型及其封装类会被复制,而引用类型,如对象、数组和集合类等不被复制;深复制就是复制所有数据,当然包括引用类型。
以下先演示浅复制。
为了以后的扩展,定义了一个继承了Cloneable的Prototype的接口。要想实现复制对象,必须实现Cloneable接口,并在实现类调用clone方法。
package come.example.prototype;
interface Prototype extends Cloneable{
public Prototype clonePrototype();
}
定义了一个Pen的类
package come.example.prototype;
public class Pen{
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}}
定义一个Student类,有基本数据类型和引用类型的成员变量
package come.example.prototype;
import java.awt.List;
import java.util.ArrayList;
public class Student implements Prototype{
private String name;
private int age;
private Pen pen;
private ArrayList<String> datas;
public ArrayList<String> getDatas() {
return datas;
}
public void setDatas(ArrayList<String> datas) {
this.datas = datas;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Pen getPen() {
return pen;
}
public void setPen(Pen pen) {
this.pen = pen;
}
public Prototype clonePrototype(){
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
}
定义了Client测试类,为Student 对象属性赋值,并调用clonePrototype()方法克隆另一个Student 的对象
package come.example.prototype;
import java.util.ArrayList;
public class Client {
public static void main(String[] args) {
Student st1 = new Student();
st1.setAge(18);
st1.setName("晓明");
Pen pen = new Pen();
pen.setColor("红色");
st1.setPen(pen);
ArrayList<String> datas = new ArrayList<String>();
datas.add("aa");
st1.setDatas(datas);
Student st2 = (Student) st1.clonePrototype();
st2.setName("小李");
st2.getPen().setColor("黄色");
ArrayList<String> d = st2.getDatas();
d.clear();
System.out.println("st1的姓名: " + st1.getName() + " 笔颜色: "+ st1.getPen().getColor() + " 数据: " + st1.getDatas().size());
System.out.println("st2的姓名: " + st2.getName() + " 笔颜色: "+ st2.getPen().getColor()+ " 数据: " + st2.getDatas().size());
}
}
打印结果是:
st1的姓名: 晓明 st1的年龄: 18 笔颜色: 黄色 数据: 0
st2的姓名: 小李 st2的年龄: 15 笔颜色: 黄色 数据: 0
从结果可以看出来,st2修改数据后,st1的姓名和年龄没有发生改变,但是笔的颜色和Arraylist的长度发生了改变,说明st1和st2的引用类型的属性指向相同的对象。这是浅复制存在的问题,而深复制能解决这个问题。
修改下代码。
Pen类也要调用clone方法进行复制
package come.example.prototype;
public class Pen implements Prototype{
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public Prototype clonePrototype() {
Prototype pen = null;
try {
pen = (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return pen;
}
}
Student类中的clonePrototype方法修改为
public Prototype clonePrototype(){
Student student = null;
try {
student = (Student) super.clone();
Pen pen = (Pen) this.pen.clonePrototype();
ArrayList<String> daList = null;
if (datas != null) {
daList = (ArrayList<String>) datas.clone();
}
student.setDatas(daList);
student.setPen(pen);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
`打印结果为
st1的姓名: 晓明 st1的年龄: 18 笔颜色: 红色 数据: 1
st2的姓名: 小李 st2的年龄: 15 笔颜色: 黄色 数据: 0
深复制的目的是,复制出来的对象和被复制的对象及其引用类型的属性都指向不同的内存地址。