0、大背景
上证50难题:有两个羊,是两个类,有相关联的特性,明确要求建立全然那样的10只羊出。
所以同时实现出来很单纯,他们先写下羊的类:
public class Sheep { private String name; private int age; private String color; //上面写上相关联的get和set方式,和相关联的内部结构器 }接著,建立10只那样的羊,就在应用程序写两个标识符建立:
//原初羊Sheep sheep =new Sheep(“tom”,1,“紫色”); //上证50 Sheep sheep1 = newSheep(sheep.getName(),sheep.getAge(),sheep.getColor());sheep1 是布季夫的第三只羊,接著就能拷贝一千遍那个标识符,接著再次命名相同的羊,以原初sheep为模版展开布季夫。
此种方式的弊病:
建立新第三类, ,组织工作效率低;常常须要再次一、蓝本商业模式
蓝本商业模式指的是,用蓝本示例选定建立第三类的种类,并透过复本那些蓝本,建立捷伊第三类;蓝本商业模式是一类建立型设计商业模式,容许两个第三类再建立另两个能订制的第三类,无须晓得怎样建立的技术细节;组织工作基本原理是:策动建立的那个第三类,允诺蓝本第三类,让蓝本第三类来他们实行创建 ,是 蓝本第三类.clone() 。如下表所示类图右图:
其中,Prototype 是两个蓝本接口,在这里面把布季夫他们的方式声明出;
ConcreteProtype 能是一系列的蓝本类,同时实现具体操作。
java 的 Object 类是所有类的根类,Object提供了两个 clone() 方式,该方式能将两个第三类拷贝一份,但是想要同时实现 clone 的 java 类必须要同时实现 Cloneable 接口,同时实现了之后那个类就具有拷贝的能力。
对于上证50难题,他们来利用蓝本设计商业模式展开改进:
让Sheep类,同时实现 Cloneable 接口:
public class Sheep implements Cloneable{ private String name; private int age; private String color; //getters&&setters&&constructors @Override protected Object clone() { Sheep sheep = null; try { sheep = (Sheep)super.clone();//使用默认Object的clone方式来完成}catch (CloneNotSupportedException e) { System.out.println(e.getMessage()); } returnsheep; } }现在的 Sheep 类是两个具体的蓝本同时实现类了,他们想要布季夫的时候,应用程序调用能这样:
Sheep sheep1 = (Sheep) sheep.clone(); Sheep sheep2 = (Sheep) sheep.clone(); //。。。。。类似此种做法是蓝本设计商业模式。
(spring框架里,透过bean标签配置类的scope为prototype,是用的蓝本商业模式)
二、蓝本商业模式的浅复本、深复本难题
使用上面所说的蓝本商业模式,按理说是拷贝出了一模那样的第三类。
但他们做两个尝试,如果 sheep 类里的成员变量有两个是第三类,而不是基础类型呢 ?
private Sheep friend;接著他们建立、再布季夫:
Sheep sheep = new Sheep(“tom”,1,“紫色”);//原初羊 sheep.setFriend(new Sheep(“jack”,2,“黑色”)); Sheep sheep1 = (Sheep) sheep.clone(); Sheep sheep2 = (Sheep) sheep.clone(); Sheep sheep3 = (Sheep) sheep.clone();重写一下 Sheep 类的 toString 方式,输出信息和相关联的特性的 hashcode 后会发现:
Sheep{name=tom, age=1, color=紫色, friend=488970385} Sheep{name=tom, age=1, color=紫色, friend=488970385} Sheep{name=tom, age=1, color=紫色, friend=488970385}friend 的 hashCode 值都那样,也是布季夫的类的 friend 特性 其实没有被拷贝,而是指向了同两个第三类。
这就叫浅拷贝(shallow copy):
对于数据类型是基本数据类型的成员变量,浅复本会直接展开值传递,也是拷贝一份给新第三类;对于数据类型是引用数据类型的成员变量,浅复本会展开引用传递,也是只是将地址指针复制一份给新第三类,实际上拷贝前和拷贝后的内容都指向同两个示例。此种情况,显然在两个第三类里修改成员变量,会影响到另两个第三类的成员变量值(因为修改的都是同两个)默认的 clone() 方式是浅复本。在源码里也说明了,那个方式是shallow copy 而不是 deep copy 。
在实际开发中,往往是希望布季夫的过程中,如果类的成员是引用类型,也能全然布季夫一份,也是所谓的 深复本 。
深复本(Deep Copy):
拷贝第三类的所有基本数据类型成员变量值;为所有 引用数据类型 的成员变量申请存储空间,并且也 拷贝每个 引用数据类型的成员变量 引用的 所有第三类 ,一直到该第三类可达的所有第三类;深复本的同时实现方式,须要透过重写 clone 方式,或者透过第三类的序列化。
上面来同时实现一下。
2.1 透过重写 clone 方式深复本
/* 被复本的类引用的类,此类的clone用默认的clone即可 */ public class CloneTarget implements Cloneable { private static final long serialVersionUID = 1L; privateString cloneName;private String cloneClass; public CloneTarget(String cloneName, String cloneClass) { this.cloneName = cloneName;this.cloneClass = cloneClass; } @Override protected Object clone() throwsCloneNotSupportedException{ return super.clone(); } }/* 蓝本类,其中有成员是引用类型,因此clone方式要重写达到深复本 */ public class Prototype implements Cloneable { public String name; public CloneTarget cloneTarget; public Prototype() { super(); } @Override protected Object clone() throws CloneNotSupportedException { Object o =null; //用了浅复本,基本数据布季夫完成,但是cloneTarget指向的还是原来的第三类 o = super.clone(); //单独处理引用类型Prototype target = (Prototype) o; target.cloneTarget = (CloneTarget)cloneTarget.clone();return target; } }这样的话,新建两个原型Prototype的第三类后,对他展开布季夫,得到的里面的 CloneTarget 成员也是深复本的两个不那样的第三类了。
但是此种方式本质上是相当于 套娃,因为都要单独处理重写 clone 方式,所以有些麻烦。
2.2 透过第三类的序列化
在 Prototype 里直接 使用序列化+反序列化 ,达到对那个第三类整体的两个拷贝。
另外注意,序列化和反序列化,必须同时实现 Serializable 接口,所以 implements 后面不止要有 Cloneable,还有Serializable。
//利用序列化同时实现深复本 public Object deepClone(){ ByteArrayOutputStream bos =null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois =null; try { bos = new ByteArrayOutputStream(); oos = newObjectOutputStream(bos); oos.writeObject(this); //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = newObjectInputStream(bis); Prototype copy = (Prototype) ois.readObject();return copy; } catch(IOException | ClassNotFoundException e) { e.printStackTrace(); }finally { try{ bos.close(); oos.close(); bis.close(); ois.close(); }catch (IOException e) { e.printStackTrace(); } } return null; }接著他们想要布季夫的时候,直接调用那个 deepClone 方式就能达到目的。
忽视掉里面的 try – catch 之类的标识符,其实核心部分是用到序列化和反序列化的总共 4 个第三类。此种方式是推荐的,因为同时实现出来更加容易。
序列化反序列化达到深复本目的的基本原理:
ObjectOutputStream 将 Java 第三类的基本数据类型和图形写入 OutputStream,但是只能将支持 java.io.Serializable 接口的第三类写入流中。在这里,他们采用的OutputStream是ByteArrayOutputStream——字节数组输出流,透过建立的ObjectOutputStream的writeObject方式,把第三类写进了那个字节数组输出流。
相相关联的,ObjectInputStream反序列化原初数据,恢复以前序列化的那些第三类。在这里,把字节数组再次内部结构成两个ByteArrayInputStream——字节数组输入流,透过ObjectInputStream的readObject方式,把输入流再次内部结构成两个第三类。
结合上面的标识符再看看:
bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos);//写入选定的OutputStreamoos.writeObject(this);//把第三类写入到输出流中,整个第三类,this bis = new ByteArrayInputStream(bos.toByteArray()); ois = newObjectInputStream(bis);//读取选定的InputStream Prototype copy = (Prototype) ois.readObject();//从输入流中读取两个第三类 return copy;三、总结
原型商业模式:
当须要建立两个捷伊第三类的内容比较复杂的时候,能利用蓝本商业模式来简化建立的过程,同时能够提高组织工作效率。因为这样不用再次初始化第三类,而是动态地获得第三类运行时的状态,如果原初的第三类内部发生变化,其他布季夫第三类也会发生相应变化,无须一 一修改。同时实现深复本的方式要注意。缺点:
每两个类都须要两个布季夫方式,对于全捷伊类来说不是难题,但是如果是用已有的类展开改造,所以可能会因为要修改源标识符而违背 OCP 原则。
文末福利:
最近整理了:Java零基础精品教学视频,技术书籍,技术文档,面试文档,学习脑图,基础核心知识等..学习资料!!
费领取。
最新Java学习资料
希望大家将此篇文章分享,转载,让更多须要的朋友看到,这样不仅帮助了他们,也帮助了他人,谢谢!!