今天老王来聊一聊原型模式,内容相对简单,相信一看就懂。
原型模式是什么?为什么要学原型模式?原型模式到底怎么做?还是按照这个学习套路来。
首先原型模式有几个关键词(克隆,拷贝,复制),故名思意,对于一个对象,我们将对他用原型模式进行克隆,拷贝,复制,相当于细胞分裂。
首先,在没有原型模式之前,我们复制对象是这样复制的(要一个,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方法没放出来)
浅克隆实现方式很简单
第一步:让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种实现方式:
我们先来看第一种:改重写的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之类的都有实现该方式进行克隆。