设计模式之原型模式与深浅拷贝
原型模式
- 原型模式
- 背景
- 克隆问题
- 改进
- 代码
- 应用场景
- 浅拷贝
- 深拷贝
- 注意事项与细节
原型模式
背景
克隆问题
类似过程:
//原始对象
User user=new User();
user.setName("...");
//...对象属性的操作;//如果需要克隆三个对象;
User user1=new User();
user1.setName(user.getName());
//...user1设置属性User user2=new User();
user2.setName(user.getName());
//...user2设置属性User user3=new User();
user3.setName(user.getName());
//...user设置属性
传统方式在创建新对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂,开发效率会变低。而且,总是需要重新初始化对象,而不是动态的获取对象运行时的状态,不够灵活;
改进
思路:Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable。
该接口表示该类能够复制且具有复制的能力 => 原型模式
1)用原型实例指定厂家对象的种类、并且通过拷贝这些原型、创建新的对象;
2)原型模式是一种创建型设计模式、允许一个对象在创建一个可定制的对象、无需知道如何创建的细节;
3)工作原理:通过将一个原型对象传给哪个要发动创建的对象、这个要发动创建的对象通过请求原型拷贝他们自己来实施创建、 对象.clone();
- Prototype : 原型类,声明一个克隆自己的接口
- ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
- Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
使用原型模式改进传统方式,让程序具有更高的效率和扩展性。
代码
package com.yxj.prototype.improve;public class Sheep implements Cloneable {private String name;private int age;private String color;private String address = "蒙古羊";public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";}//克隆该实例,使用默认的clone方法来完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {// TODO: handle exceptionSystem.out.println(e.getMessage());}// TODO Auto-generated method stubreturn sheep;}}package com.yxj.prototype.improve;public class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");// TODO Auto-generated method stubSheep sheep = new Sheep("tom", 1, "白色");Sheep sheep2 = (Sheep)sheep.clone(); //克隆Sheep sheep3 = (Sheep)sheep.clone(); //克隆Sheep sheep4 = (Sheep)sheep.clone(); //克隆Sheep sheep5 = (Sheep)sheep.clone(); //克隆System.out.println("sheep2 =" + sheep2);System.out.println("sheep3 =" + sheep3);System.out.println("sheep4 =" + sheep4);System.out.println("sheep5 =" + sheep5);}}
应用场景
在SpringIOC容器中,bean的类型有singleton,还有prototype;
前者是单例,后者是原型,每次从容器中获取的对象都是新的对象。
if (mbd.isSingleton()) {sharedInstance = this.getSingleton(beanName, () -> {try {return this.createBean(beanName, mbd, args);} catch (BeansException var5) {this.destroySingleton(beanName);throw var5;}});bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {var11 = null;Object prototypeInstance;try {this.beforePrototypeCreation(beanName);prototypeInstance = this.createBean(beanName, mbd, args);} finally {this.afterPrototypeCreation(beanName);}bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
浅拷贝
- 对于数据类型是基本数据类型的成员变量、浅拷贝会直接进行值传递、将值复制一份给新的对象;
- 如果是引用数据类型的成员变量、浅拷贝会进行引用传递、也就是将该成员变量的引用值(内存地址)复制一份给新的对象。实际上两个对象的该成员变量都指向同一个实例。
- 在这种情况下、在一个对象中修改该成员变量会影响另一个对象的成员变量值;
package com.yxj.prototype.improve;public class Sheep implements Cloneable {private String name;private int age;private String color;private String address = "蒙古羊";public Sheep friend; //是对象, 克隆是会如何处理public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";}//克隆该实例,使用默认的clone方法来完成@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {// TODO: handle exceptionSystem.out.println(e.getMessage());}// TODO Auto-generated method stubreturn sheep;}}package com.yxj.prototype.improve;public class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");// TODO Auto-generated method stubSheep sheep = new Sheep("tom", 1, "白色");sheep.friend = new Sheep("jack", 2, "黑色");Sheep sheep2 = (Sheep)sheep.clone(); //克隆Sheep sheep3 = (Sheep)sheep.clone(); //克隆Sheep sheep4 = (Sheep)sheep.clone(); //克隆Sheep sheep5 = (Sheep)sheep.clone(); //克隆System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());}}
这是浅拷贝,属性friend引用都指向堆中的同一个对象;
深拷贝
1)复制对象的所有基本数据类型的成员变量值;
2)为所有引用数据类型的成员变量申请存储空间、并复制每个引用类型成员变量所引用的对象,直到该对象可达的所有对象;也就是、对象进行深拷贝就是对整个对象进行拷贝;
3)深拷贝实现方式:重写Clone方法来实现深拷贝;
4)深拷贝实现方式2:通过对象序列化实现深拷贝;
package com.yxj.prototype.deepclone;import java.io.Serializable;public class DeepCloneableTarget implements Serializable, Cloneable {/*** */private static final long serialVersionUID = 1L;private String cloneName;private String cloneClass;//构造器public DeepCloneableTarget(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}package com.yxj.prototype.deepclone;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class DeepProtoType implements Serializable, Cloneable{public String name; //String 属性public DeepCloneableTarget deepCloneableTarget;// 引用类型public DeepProtoType() {super();}//深拷贝 - 方式 1 使用clone 方法@Overrideprotected Object clone() throws CloneNotSupportedException {Object deep = null;//这里完成对基本数据类型(属性)和String的克隆deep = super.clone(); //对引用类型的属性,进行单独处理DeepProtoType deepProtoType = (DeepProtoType)deep;deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();// TODO Auto-generated method stubreturn deepProtoType;}//深拷贝 - 方式2 通过对象的序列化实现 (推荐)public Object deepClone() {//创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //当前这个对象以对象流的方式输出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType)ois.readObject();return copyObj;} catch (Exception e) {// TODO: handle exceptione.printStackTrace();return null;} finally {//关闭流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {// TODO: handle exceptionSystem.out.println(e2.getMessage());}}}}package com.yxj.prototype.deepclone;public class Client {public static void main(String[] args) throws Exception {// TODO Auto-generated method stubDeepProtoType p = new DeepProtoType();p.name = "宋江";p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");//方式1 完成深拷贝// DeepProtoType p2 = (DeepProtoType) p.clone();
//
// System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
// System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());//方式2 完成深拷贝DeepProtoType p2 = (DeepProtoType) p.deepClone();System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());}}
注意事项与细节
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是动态地获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意
设计模式之原型模式与深浅拷贝相关推荐
- 设计模式之原型模式以及深浅拷贝的区别
原型模式也是创建对象的一种方式,它一般用在这样的场景:系统中存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂而且耗费资源.这个时候使用原型模式的克隆方式,能够节省不少时间. ...
- 设计模式学习笔记(六)原型模式以及深浅拷贝的区别
原型模式也是创建对象的一种方式,它一般用在这样的场景:系统中存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂而且耗费资源.这个时候使用原型模式的克隆方式,能够节省不少时间. ...
- 设计模式_4_原型模式(对象的拷贝)
原形模式(PrototypePattern, 创建型模式,创建重复对象且保证性能, 对象的克隆) 通常使用原型模式创建一个原型接口, 用于获取创建对象的克隆, 对于浅拷贝与深拷贝不用纠结, 他们二者的 ...
- 原型模式 java 深浅_java学习笔记之原型模式及深浅拷贝
一.原型模式的基本介绍 在聊原型模式之前,我们来思考一个小问题,传统的方式我们是如何克隆对象呢? 那我们以多利羊(Sheep)为例,来说明上述这个问题,具体代码见下面: 多利羊(Sheep) publ ...
- Head First设计模式之原型模式
一.定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式.在实际应用中,原型模式很少单独出现 ...
- java 肌汉模式_设计模式之原型模式详解(附源代码)
原型模式(Prototype Pattern) 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. ...
- 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)
[索引页] [源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:webabcd 介绍 用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象. ...
- 教你如何一篇博客读懂设计模式之—--原型模式
教你如何一篇博客读懂设计模式之----原型模式 what:是什么 原型模式: 用于创建重复的对象,既不用一个属性一个属性去set和get,又不影响性能,原型模式产生的对象和原有的对象不是同一个实例,他 ...
- C#设计模式(6)——原型模式(Prototype Pattern)
一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在 ...
最新文章
- 如何绕过chrome的弹窗拦截机制
- HihoCode1721删除一个字符之后是回文字符串
- UVa OJ 120
- ActiveMQ点对点消息通信demo
- 不常见但是有用的 Chrome 调试技巧
- MySQL的CSV引擎应用实例解析
- com.android.pngp.tln,Android资源之图像资源(图像级别资源)
- R语言·文本挖掘︱Rwordseg/rJava两包的安装(安到吐血)
- hive表中加载数据
- TCP/IP协议新手入门学习
- hadoop常用端口号
- html5橡皮擦,用HTML5 Canvas API中的clearRect()方法实现橡皮擦功能
- 一个美式英语发音的app开源
- 【Qt设计开发】GUI界面设计开发
- WannaCry爆发一年后,EternalBlue的威胁仍然存在
- 【读书】2020年阅读记录及心得
- 手把手带你开发一个批量下载资源的谷歌浏览器扩展
- Xcode各版本和IOS模拟器个版本下载
- 亚马逊测评自养号环境系统的介绍和用法
- 关于腾讯TBS中,加载失败问题(64位手机无法加载x5)