Java 设计模式之原型模式
一、简介
用一个已经创建的实例作为原型,通过复制改原型对象来创建一个和原型对象相同的新对象。
原型模式是创建型模式的一种,主要用于创建重复的对象,
主要角色:
抽象原型类:规定具体原型对象必须实现的clone()方法。
具体原型类:实现抽象原型的clone()方法。
访问类:使用具体原型类的clone()方法复制出新的对象。
二、实现
原型模式可以分为浅克隆和深克隆:
浅克隆:创建一个新对象,新对象的属性和原对象完全相同,但是非基本数据类型,仍指向原有属性对象的内存地址。
深克隆:创建一个新对象,属性中的对象也会被克隆,不指向原对象的内存地址。
三、浅克隆
举例:
比如我有一个老婆,这个老婆身材完美,前凸后翘,但是一个老婆满足不了我,我想多要一个一模一样的老婆,此时可以通过原型模式复制出新的老婆。
代码实现:
抽象原型类:
java的Object类中提供了clone()方法来实现克隆,Cloneable接口是指上面说到的抽象原型类,所以我们使用Cloneable当作抽象原型类。
具体原型类:
//老婆类 实现Cloneable接口 public class Wife implements Cloneable { private int age = 20; private int height = 165; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } //重写clone方法 @Override public Wife clone() throws CloneNotSupportedException { return (Wife)super.clone(); } @Override public String toString() { return "Wife{" + "age=" + age + ", height=" + height + '}'; } }
使用:
public static void main(String[] args) throws CloneNotSupportedException { //创建原型对象 Wife wife = new Wife(); //通过原型对象创建新对象 Wife wife2 = wife.clone(); //要一个大一些的 wife2.setHeight(170); }
输出:
Wife{age=20, height=165} Wife{age=20, height=170}
此时通过原型模式(浅拷贝)就创建出了我的第二个身高不同老婆。非常满意。
四:深克隆
举例:
为了验证深浅克隆的区别,我们把Wife中的参数封装成对象试试看。
//老婆属性 public class Param implements Serializable{ private int age = 20; private int height = 165; public Param(int age,int height){ this.age = age; this.height = 165; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } @Override public String toString() { return "Param{" + "age=" + age + ", height=" + height + '}'; } } //老婆类 public class Wife implements Cloneable,Serializable { //老婆属性 private Param param; public Param getParam() { return param; } public void setParam(Param param) { this.param = param; } @Override public Wife clone() throws CloneNotSupportedException { return (Wife)super.clone(); } }
使用:
public static void main(String[] args) throws CloneNotSupportedException { //创建原型对象 Wife wife = new Wife(); //设置属性 wife.setParam(new Param(20,165)); //通过原型对象创建新对象 Wife wife2 = wife.clone(); //修改新对象的身高 wife2.getParam().setHeight(170); System.out.println(wife.getParam().toString()); System.out.println(wife2.getParam().toString()); }
输出:
Param{age=20, height=170} Param{age=20, height=170}
为什么只修改了wife2的身高,wife的身高也变了呢?这怎么行,原因就是因为目前还是浅克隆,写这段代码就是为了验证深浅克隆,可以看出 wife中的params只要修改了一个,就都会发生变化,可见两个param所指向的内存地址还是相同的。解决办法就是深克隆。
深克隆实现:
深克隆可以使用流的方式来实现,只需要在原型类中的clone方法中来实现即可。
@Override public Wife clone() throws CloneNotSupportedException { //获取clone的对象 Wife wife = (Wife) super.clone(); try{ //把对象写入到字节流中 ByteArrayOutputStream baos =new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(wife); //把字节流转化为对象 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); return (Wife) ois.readObject(); }catch (IOException | ClassNotFoundException e){ e.printStackTrace(); } return wife; }
使用输出:
Param{age=20, height=165} Param{age=20, height=170}
此时通过原型模式的深克隆就得到了两个身高不一样的老婆。