作者:东风玖哥,小灰

来源:程序员小灰

—————  第二天  —————

————————————

假如有一天,小灰被外星人抓走了,外星人要拿小灰做实验,想了解小灰在吃得好、睡得好、玩得开心的场景下,与现实中小灰的生存状态有什么区别。

于是,外星人克隆了几个一模一样的小灰:

就这样,小灰的原型被留在现实中,而三个复制体分别提供了吃得好、睡得好、玩得开心三种不同环境,小灰的原型则不受三个复制体的影响。

过了一段时间,我们来观察一下本体与分身的生存状态:

在Java语言中,Object类实现了Cloneable接口,一个对象可以通过调用Clone()方法生成对象,这就是原型模式的典型应用。但需要注意的是,clone()方法并不是Cloneable接口里的,而是Object类里的,Cloneable是一个标识接口,标识这个类的对象是可被拷贝的,如果没有实现Cloneable接口,却调用了clone()方法,就会报错。

// protected native Object clone() throwsCloneNotSupportedException;protected Object clone() throws
CloneNotSupportedException {if (!(this instanceof Cloneable)) {throw new CloneNotSupportedException(
"Class " + getClass().getName() +
" doesn't implement Cloneable");}return internalClone();
}// Native helper method for cloning.private native Object internalClone();

Java中的数据类型,分为基本类型和引用类型。在一个方法里的变量如果是基本类型的话,变量就直接存储在这个方法的栈帧里,例如int、long等;而引用类型则在栈帧里存储这个变量的指针,指向堆中该实体的地址,例如String、Array等。深拷贝和浅拷贝是只针对引用数据类型的。

比如一个方法有一个基本类型参数和一个引用类型参数,在方法体里对参数重新赋值,会影响传入的引用类型参数,而不会影响基本类型参数,因为基本类型参数是值传递,而引用类型参数是引用传递。

先定义一个用户类:

// 这是一个非常简单的用户类public class User {private String name;private int age;public User(String name, int age) {this.name=name;this.age=age;}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;}@Overridepublic String toString() {return "User{name='" + name + ", age=" + age +'}';}}
private int x=10;public void updateValue(int value){value = 3 * value;
}private User user= new User("大黄",20);public void updateUser(User student){student.setName("小灰");student.setAge(18);
}public void test(){System.out.println("调用前x的值:"+x);updateValue(x);System.out.println("调用后x的值:"+x);System.out.println("调用前user的值:"+user.toString());updateUser(user);System.out.println("调用后user的值:"+user.toString());}

测试:

Log打印结果如下:

调用前x的值:10
调用后x的值:10
调用前user的值:User{name='大黄, age=20}
调用后user的值:User{name='小灰, age=18}

传递基本类型的方法(updateValue())流程图:

传递引用类型的方法(updateUser())流程图:

这其中也包含着例外,比如String类型和大小不超过127的Long类型,虽然也是引用类型,却像基本类型一样不受影响。这是因为它们会先比较常量池维护的值,这涉及VM的内容,今天不做过多讨论。

浅拷贝是在按位(bit)拷贝对象,这个对象有着原始对象属性值的一份精确拷贝。我们结合应用场景分析一下,还是刚才的User类,我们增加一个存放地址的内部类Address,我们需要用户信息可以被其他module查询,但是不允许它们被其他module修改,新增代码如下:

// 这是一个稍微复杂的、支持拷贝的用户类public class User implements Cloneable {
// ……省略上文代码……private Address address;@NonNull@NotNull@Overridepublic User clone() {try{return (User)super.clone();}catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}public class Address{// 地市public String city;// 区县public String county;// 乡镇街道public String street;}
}

// 这是一个更复杂的、支持深拷贝的用户类public class User implements Cloneable {// ……省略上文代码……@NonNull@NotNull@Overridepublic User clone() {try{User newUser = (User)super.clone();newUser.setName(this.name);newUser.setAddress(this.address.clone());return newUser;}catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}public class Address implements Cloneable{// ……省略上文代码……@NonNull@NotNull@Overridepublic Address clone() {try{Address newAddress = (Address)super.clone();newAddress.city = this.city;newAddress.county = this.county;newAddress.street = this.street;return newAddress;}catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}}
}

需要注意的是,上面代码的深拷贝其实并不彻底,因为彻底的深拷贝几乎是不可能实现的,那样不但可能存在引用关系非常复杂的情况,也可能存在引用链的某一级上引用了一个没有实现Cloneable接口的第三方对象的情况。

绝大多数设计模式都是牺牲性能提升开发效率的,原型模式则是为数不多的牺牲开发效率提升性能的设计模式。

private User user= new User("大黄",20);public void testNew(){User user1 = new User("小灰",18);}public void testClone(){User user2 = user.clone();}

通过ASM工具查看bytecode,可以看出二者对栈资源的消耗:

// access flags 0x1public  testNew()V……省略……MAXSTACK  = 4MAXLOCALS = 2// access  flags 0x1public  testClone()V……省略……MAXSTACK  = 1MAXLOCALS = 2
}

@Overridepublic Object clone() {return new Intent(this);}

最后我们来总结一下原型模式的核心用途:

1.解决构建复杂对象的资源消耗问题,提升创建对象的效率。

2.保护性拷贝,防止外部对只读对象进行需修改。

什么是 “原型模式” ?相关推荐

  1. GOF23设计模式(创建型模式) 原型模式

    目录 一:原型模式的定义 二:关于深克隆和深克隆的区别 三:反序列化的方式实现深克隆 原型模式: 通过new产生一个对象需要非常繁琐的数据准备或访冋权限,则可以使用原型模式. 就是java中的克隆技术 ...

  2. 大话设计模式笔记(七)の原型模式

    举个栗子 问题描述 要求有一个简历类,必须要有姓名,可以设置性别和年龄,可以设置工作经历,最终需要三份简历. 简单实现 简历类 /*** 简历类* Created by callmeDevil on ...

  3. 设计模式 之美 -- 原型模式

    文章目录 1. 解决问题 2. 应用场景 3. 实现方式 C++实现 C语言实现 4. 缺点 5. 和其他三种创建模式的对比(单例,工厂,建造者) 1. 解决问题 如果对象的创建成本较大,而同一个类的 ...

  4. 设计模式之原型模式(Prototype)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式包括:1.FactoryMethod(工厂方法模式):2.Abstract Factory(抽象工厂模式):3.Sin ...

  5. 设计模式入门之原型模式Prototype

    //原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象 //简单来说,当进行面向接口编程时,假设须要复制这一接口对象时.因为不知道他的详细类型并且不能实例化一个接口 //这时就须要 ...

  6. 大战设计模式【23】—— 原型模式

    原型模式(Prototype) 设计模式使用的例子https://github.com/LinkinStars/DesignPatternsAllExample 一.定义 使用原型实例指定创建对象的种 ...

  7. 10Prototype(原型)模式

    技术交流QQ群:1027579432,欢迎你的加入! 1.Prototype(原型)模式动机 在软件系统中,经常面临着某些结构复杂对象的创建工作,由于需求的变化,这些对象经常面临着剧烈的变化,但是它们 ...

  8. 设计模式之原型模式prototype

    1.原型模式的使用和本质.以及优势: a.通过 new 产生一个对象需要非常繁琐的数据准备或者访问权限,则可以使用原型模式. b.原型模式的使用就是 java 中的克隆技术,以某个对象为原型,复制出新 ...

  9. (二十三)原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  10. 设计模式---原型模式(Prototype Pattern)

    在编程中有时候我们会发现,当我们需要一个实例,可是这个实例的创建过程十分复杂,在执行过程中 会消耗大量的时间,同时创建第一个实例和创建第二个时间的初始化信息并未改变.在此种情况下,直接New 一个实例 ...

最新文章

  1. java 运算符_java 运算符
  2. Canal全方位深入讲解,看这一篇就够了
  3. English debate sample motion
  4. SAP Spartacus B2B List里的listData$设计原理
  5. mysql默认值无效_MySQL开发规范
  6. 优先队列默认是小顶堆吗_一分钟带你读懂什么是堆?
  7. SAP License:SAP安装前添加虚拟网卡步骤
  8. Base64 转图片
  9. python cpk 计算
  10. 詹姆斯titan_再见,詹姆斯!
  11. 学以致用——英文姓名高词频分析-使用Excel制作高频词标签云(VBA)
  12. 利用几何布朗运动模型预测股票价格_20200514_
  13. XSS攻击原理及防范
  14. 约束(Constraint)SQL约束有哪几种?【常用的约束】【有例子】【非空约束】【唯一约束】【主键约束】【外键约束】【检查约束】
  15. 基于深度学习的手写数字实现及超简单的英文字母识别
  16. 【天池竞赛】心跳数据挖掘
  17. 家常菜做法:熬萝卜粉丝
  18. 优秀员工的十二种品质 - 马云在上海演讲说的话
  19. 自动化办公 | 快速从Excel中提取图片并匹配命名
  20. 二进制数的补码及运算

热门文章

  1. C++数据的一些注意事项
  2. 造轮子是什么意思_程序员为什么热衷于造轮子,升职加薪吗?
  3. php七牛分片上传_七牛视频切片方案 - 张小超fly的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. z370支持pcie信号拆分吗_定了!AMD B550主板确认将支持PCIE4.0,多项能力接近X570
  5. php sphinx api调用,php调用Sphinx
  6. 自科基金大比拼!下一轮双一流,谁能上车?谁会下车?
  7. Nature:给博士研究生的四条箴言Four golden lessons,颜宁:写的真好!
  8. 开挂的人生: 本科生发Nature和 Science
  9. linux取消线程的原理,浅析 Linux 进程与线程
  10. java排序算法原理_排序算法原理与实现(java)