首先来看其定义specify the kinds of objects to create using a prototypical instance, and create new objects by coping this protype。一句话总结就是采用复制已有对象实例来产生新的对象实例就被称为原型模式。
java系统中,一个类可以通过复制实现生成新的对象,必须继承Cloneabel接口。事实上,Cloneable接口并不包含任何方法,它只是作为一种“标记”特征,告诉JVM,继承了该接口的对象可能可以调用clone方法复制产生新的对象。之所以说可能可以是因为该类必须覆写clone方法,才可以真的被clone。此处需要说明的是,Cloneabel接口不不包含任何方法,clone方法是来源于Object父类。
示例代码如下:
public class CloneModel implements Cloneable{
@Override
protected CloneModel clone(){
CloneModel cloneModel = null;
try{
cloneModel = (CloneModel)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
return cloneModel;
}
public static void main(String [] args){
CloneModel cloneModel = new CloneModel();
CloneModel cloneModel1 = cloneModel.clone();
}
}
clone方式相对new方式产生新的对象实例的优点在于,前者是直接对内存中二进制流的复制而不需要通过构造器生成对象,其效率自然更高一些。也由于前者产生一个新的对象时,并不执行构造函数,所以它也可以逃避构造函数的约束。谈到这里,不免想到我们前面的单例模式,在后面会讲到,只要不手动复制其被final关键字修饰的成员变量,如果该单例模式实现了Cloneable接口且覆写了clone()方法(当然我们肯定不会这么做),我们将打破单例模式的限制。
既然是通过复制实现的创建新的对象,我们不免想到该对象所附属的成员变量值会不会一同被复制呢?下面用一个例子说明:
public class CloneModel implements Cloneable{
private ArrayList<String> arrayList = new ArrayList<String>();
private String str;
private int num_int = 0;
private Integer num_integer = 0;
public String getArrayList() {
return arrayList.toString();
}
public void setArrayList(String str) {
this.arrayList.add(str);
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public int getNum_int() {
return num_int;
}
public void setNum_int(int num_int) {
this.num_int = num_int;
}
public Integer getNum_integer() {
return num_integer;
}
public void setNum_integer(Integer num_integer) {
this.num_integer = num_integer;
}
@Override
protected CloneModel clone(){
CloneModel cloneModel = null;
try{
cloneModel = (CloneModel)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
return cloneModel;
}
public static void main(String [] args){
//原始对象赋值
CloneModel model = new CloneModel();
model.setArrayList("First");
model.setNum_int(1);
model.setNum_integer(1);
model.setStr("First");
//打印复制后的对象各成员变量值
System.out.println("clone后,clone对象值:");
CloneModel cloneModel = model.clone();
System.out.println(cloneModel.getArrayList());
System.out.println(cloneModel.getNum_int());
System.out.println(cloneModel.getNum_integer());
System.out.println(cloneModel.getStr());
//改变复制后的对象各成员变量值
cloneModel.setArrayList("Second");
cloneModel.setNum_int(2);
cloneModel.setNum_integer(2);
cloneModel.setStr("Second");
//打印原始对象成员变量值
System.out.println("改变clone对象值之后,原始对象值:");
System.out.println(model.getArrayList());
System.out.println(model.getNum_int());
System.out.println(model.getNum_integer());
System.out.println(model.getStr());
}
}
在例子中,我们定义了四个成员变量,并分别在复制前改变原始对象赋值以及改变clone对象赋值,先后打印复制后的对象值以及原始对象值,结果如下:
为什么我们改变复制得到的对象的成员变量,结果原始对象的部分成员变量值也随之发生了改变呢?
原因在于,Object类的clone()方法所实现的复制,在成员变量的复制上,它只复制了值传递变量的值,并为之开辟了新的内存空间,也就是这些值引用的成员变量与之前的原始对象再无半点瓜葛。而针对引用传递的变量,它只复制了其引用而并未为该变量创建新的内存空间,也就是说,它与原始对象共享了引用传递变量的内存区域,就有了上面的“大家一起变”的情况。
若非得将引用传递类型的成员变量搞成“你是你 我是我”,像值传递变量一样的话,只需要在调用super.clone()方法时,单独调用该引用传递对象成员变量的clone()方法,并将其赋值到新的clone对象的成员变量上。如下:
@Override
protected CloneModel clone(){
CloneModel cloneModel = null;
try{
cloneModel = (CloneModel)super.clone();
cloneModel.arrayList = (ArrayList<String>)this.arrayList.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
return cloneModel;
}
需要注意的是,若该变量恰好被final关键字修饰,则编译器将报错。此时,最好做法是去除final 关键字。