一、引言
首先原型模式有几个关键词( **克隆,拷贝,复制 **),故名思意,对于一个对象,我们将对他用原型模式进行克隆,拷贝,复制,相当于细胞分裂。
首先,在没有原型模式之前,我们复制对象是这样复制的(要一个,new一个):
//这是老王类
public class Laowang {
private String name;
private String city;
private School school;
public Laowang(String name, String city, School school) {
this.name = name;
this.city = city;
this.school = school;
}
}
//Main方法
public static void main(String[] args) {
Laowang laowang=new Laowang("老王", "广东广州", new School("广州某大学"));
Laowang laowang1=new Laowang("老王", "广东广州", new School("广州某大学"));
Laowang laowang2=new Laowang("老王", "广东广州", new School("广州某大学"));
}
这样复制对象,有什么缺陷呢?比如每次我们都要整齐输入构造对象的参数“老王”,"广东广州","广州某大学"。这样显然过于笨重,因此原型模式出现了。接下来聊聊怎么实现。
二、实现原型模式
接下来介绍原型模式的2种实现方式(部分构造方法,set方法没放出来)
-
浅克隆(不会复制属性是对象引用)
-
深克隆(会复制属性是对象的引用)
1、浅克隆
浅克隆实现方式很简单 第一步:让Laowang类 **实现Cloneable接口 ** 第二步: **重写父类clone方法 **(任何一个对象都是继承于Object,因此都有clone方法)。 代码如下:
//这是Laowang类
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//重写父类clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//这是Main方法
public static void main(String[] args) throws CloneNotSupportedException {
//原型模式浅克隆
Laowang laowang3= (Laowang) laowang.clone();
System.out.println("-------------------浅克隆------------------------");
System.out.println("本体的信息"+laowang.toString());
System.out.println("浅克隆的信息"+laowang3.toString());
}
运行结果如下: 可以看到,克隆是克隆了,但是属性 **School **并没有完全克隆,因为始终指向和本体同一个School对象。 在有属性是对象引用的情况下,这是浅克隆的劣势。
2、深克隆
深克隆的意思就会把原来的对象引用也同时克隆。 深克隆有2种实现方式:
- 修改重写的clone方法方式。
- 利用对象流方式深克隆
我们先来看第一种:改重写的clone方法方式 直接上代码 **让School类也实现Cloneable接口,重写父类clone方法 **
//这是School类
public class School implements Cloneable{
private String schoolName;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
**Laowang类重写的clone方法进行修改: **
//这是Laowang类
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//重写的clone方法进行修改
@Override
protected Object clone() throws CloneNotSupportedException {
School s1 = (School) school.clone();//让school自己先复制一个对象
Laowang l1 = (Laowang) super.clone();//让自己也复制一个对象
l1.setSchool(s1);//把刚才School复制后的对象赋值给l1
return l1;//返回l1
}
}
//这是Main方法
public static void main(String[] args) throws CloneNotSupportedException {
//原型模式深克隆1
Laowang laowang3= (Laowang) laowang.clone();
System.out.println("-------------------深克隆1------------------------");
System.out.println("本体的信息"+laowang.toString());
System.out.println("深克隆的信息"+laowang3.toString());
}
运行结果如下: 很明显,对象引用地址不同,已经发生了深度克隆。 这种做法其实不太被推荐,为什么?因为如果属性有一个对象引用,至少就 要修改对象引用类继承Cloneable接口。
因此更推荐下面做法:通过对象流进行深度克隆 代码如下: **让所有类都实现Serializabel接口(支持序列化和反序列化) **
public class School implements Serializable {
private String schoolName;
public School(String schoolName) {
this.schoolName = schoolName;
}
}
**写一个深度克隆deepClone方法如下 **
//这是Laowang类
public class Laowang implements Cloneable{
private String name;
private String city;
private School school;
//深度克隆
public Laowang deepClone() 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 (Laowang) ois.readObject();
}
}
//这是Main方法
public static void main(String[] args) throws IOException, ClassNotFoundException {
//模式克隆2
Laowang laowang3= (Laowang) laowang.deepClone();
System.out.println("-------------------深克隆2------------------------");
System.out.println("本体的信息"+laowang.toString());
System.out.println("深克隆的信息"+laowang3.toString());
}
运行结果如下图: 可以看到,也是有进行了对象引用的深度克隆。
三、结束语
原型模式很好理解,就是一个对象的复制。只不过复制的方法有所不同。原型模式在java底层也有用到,例如map,set之类的都有实现该方式进行克隆。