002-创建型-05-原型模式(Protype)
一、概述
指原型实例指定创建对象的种类,并通过克隆这些原型创建新的对象
原型模式就是让类实现Cloneable接口,达到克隆原型类的方式。
1.1、适用场景
1、在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
2、希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。
3、隐藏克隆操作的细节,很多时候,对对象本身的克隆需要涉及到类本身的数据细节。
4、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;
5、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。
7、循环体中生产大量对象时
1.2、优缺点
优点:
- 当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高实例的创建效率。
- 扩展性好。由于原型模式提供了抽象原型类,在客户端针对抽象原型类进行编程,而将具体原型类写到配置文件中,增减或减少产品对原有系统都没有影响。
- 可以使用深克隆方式保存对象的状态。使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
- 需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了“开闭原则”。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
1.3、深浅克隆
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
总之深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆(递归性的)。
1.4、示例
1.4.1、浅克隆
public class PersonShallow implements Cloneable {
private String name;
private Integer age;
private Date birthday;
public PersonShallow() {
System.out.println("ctor PersonShallow");
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "PersonShallow{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
测试
@Test
public void test() throws Exception {
//原型A对象
PersonShallow a = new PersonShallow();
a.setName("李宏旭");
a.setAge(1);
a.setBirthday(new Date());
System.out.println("a:" + a);
System.out.println("*************克隆**************"); //克隆B对象
PersonShallow b = (PersonShallow) a.clone();
System.out.println("b:" + b);
System.out.println("***************比较***************");
/*
* 测试A==B?
* 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象
*/
System.out.print("比较:A==B?");
System.out.println(a == b?"是一个对象":"不是一个对象");
System.out.println("a.hashCode:"+a.hashCode());
System.out.println("b.hashCode:"+b.hashCode()); b.getBirthday().setTime(666666666666L);
System.out.println("a Birthday为:" + a.getBirthday());
System.out.println("b Birthday为:" + b.getBirthday()); a.setName("李四");
System.out.println("a name 为:" + a.getName());
System.out.println("b name 为:" + b.getName()); /*
* 比较Date对象
*/
System.out.print("比较:A.Date==B.Date?");
System.out.println(a.getBirthday() == b.getBirthday()?"是一个对象":"不是一个");
}
输出
ctor PersonShallow
a:PersonShallow{name='李宏旭', age=1, birthday=Tue Jul 09 17:25:43 CST 2019}
*************克隆**************
b:PersonShallow{name='李宏旭', age=1, birthday=Tue Jul 09 17:25:43 CST 2019}
***************比较***************
比较:A==B?不是一个对象
a.hashCode:1811075214
b.hashCode:1588970020
a Birthday为:Sat Feb 16 09:11:06 CST 1991
b Birthday为:Sat Feb 16 09:11:06 CST 1991
a name 为:李四
b name 为:李宏旭
比较:A.Date==B.Date?是一个对象
可以发现B在克隆A的birthday时,是直接克隆的引用。这种是浅克隆。
说明点:
1、(PersonShallow) a.clone();这样子克隆并不等同于Person p2 = p1;像Person p2 = p1;指的是在栈中创建一个变量p2,将p1的内存地址赋给p2,其实指的是同一个对象。而克隆是复制出一份一模一样的对象,两个对象内存地址不同,但对象中的结构与属性值一模一样。
2、对象克隆拷贝时,类的构造函数是不会被执行的。
3、当被克隆的类中有引用对象(String或Integer等包装类型除外)时,克隆出来的类中的引用变量存储的还是之前的内存地址如Date,也就是说克隆与被克隆的对象是同一个。这样的话两个对象共享了一个私有变量,所有人都可以改,是一个种非常不安全的方式,在实际项目中使用还是比较少的。
1.4.2、深拷贝
示例
public class PersonDeep implements Cloneable {
private String name;
private Date birthday;
private List<String> list;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
@Override
protected Object clone() throws CloneNotSupportedException {
PersonDeep deep = null;
try {
deep = (PersonDeep) super.clone();
// 需要特殊处理引用类型的变量
deep.birthday = (Date) deep.birthday.clone();
List<String> newList = new ArrayList();
if(this.list!=null)
for (String str : this.list) {
newList.add(str);
}
deep.setList(newList);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return deep;
}
@Override
public String toString() {
return "PersonDeep{" +
"name='" + name + '\'' +
", birthday=" + birthday +
", list=" + list +
'}';
}
}
测试
@Test
public void test() throws Exception {
//原型A对象
PersonDeep a = new PersonDeep();
a.setName("李宏旭");
a.setBirthday(new Date());
System.out.println("a:" + a);
System.out.println("*************克隆**************"); //克隆B对象
PersonDeep b = (PersonDeep) a.clone();
System.out.println("b:" + b);
System.out.println("***************比较***************");
/*
* 测试A==B?
* 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象
*/
System.out.print("比较:A==B?");
System.out.println(a == b?"是一个对象":"不是一个对象");
System.out.println("a.hashCode:"+a.hashCode());
System.out.println("b.hashCode:"+b.hashCode()); b.getBirthday().setTime(666666666666L);
System.out.println("a Birthday为:" + a.getBirthday());
System.out.println("b Birthday为:" + b.getBirthday()); a.setName("李四");
System.out.println("a name 为:" + a.getName());
System.out.println("b name 为:" + b.getName()); /*
* 比较Date对象
*/
System.out.print("比较:A.Date==B.Date?");
System.out.println(a.getBirthday() == b.getBirthday()?"是一个对象":"不是一个");
}
输出
a:PersonDeep{name='李宏旭', birthday=Tue Jul 09 18:03:23 CST 2019, list=null}
*************克隆**************
b:PersonDeep{name='李宏旭', birthday=Tue Jul 09 18:03:23 CST 2019, list=[]}
***************比较***************
比较:A==B?不是一个对象
a.hashCode:1811075214
b.hashCode:1588970020
a Birthday为:Tue Jul 09 18:03:23 CST 2019
b Birthday为:Sat Feb 16 09:11:06 CST 1991
a name 为:李四
b name 为:李宏旭
比较:A.Date==B.Date?不是一个
这样就完成了深度拷贝,两种对象互为独立,属于单独对象。
注意:final 类型修饰的成员变量不能进行深度拷贝
二、扩展
2.1、JDK1.8源码中的原型模式
ArrayList的clone()
HashMap的clone()
CacheKey的clone()
002-创建型-05-原型模式(Protype)的更多相关文章
- JAVA设计模式 2【创建型】原型模式的理解与使用
在本节中,我们将学习和使用原型模式:这一节学习的原型模式也是创建型 模式的其中之一.再次复习一下:创建型 模式就是描述如何去更好的创建一个对象. 我们都知道,在JAVA 语言中.使用new 关键字创建 ...
- 设计模式01 创建型模式 - 原型模式(Protype Pattern)
参考 1. 设计模式:原型模式 | 博客园 2. Java clone深拷贝.浅拷贝 | CSDN 3. Cloneable接口和Object的clone()方法 | 博客园 原型模式(Prototy ...
- 05原型模式Prototype
一.什么是原型模式 Prototype模式是一种对象创建型模式,它采 取复制原型对象的方法来创建对象的实例.使用 Prototype模式创建的实例,具有与原型一样的 数据. 二.原型模式的特点 1. ...
- GoLang设计模式05 - 原型模式
原型模式也是一种创建型模式,它可以帮助我们优雅地创建对象的拷贝.在这种设计模式里面,将克隆某个对象的职责交给了要被克隆的这个对象.被克隆的对象需要提供一个clone()方法.通过这个方法可以返回该对象 ...
- 【创建型】Prototype模式
原型模式主要是用原型实例指定创建原型对象的种类,并且通过拷贝原型创建新对象.最简单的理解就是克隆.就如cocos2d-x中的 class Clonable::clone();该模式的主要目的是可以在运 ...
- 【创建型】Builder模式
生成器模式的主要思想:将产品对象的创建与表现分离开,并且同样的创建过程可以有不同的产品表现. 直白一点可以理解为:待创建的对象是复杂的,一般情况下是需要经过多个步骤的创建后,最终才能将完整产品创建好, ...
- 【创建型】Singleton模式
单例模式可以说是所有23种设计模式中最为简单的一个,没有之一.其主要思想就是保证整个应用环境中,最多只会有一个对象的实例.类关系图参考如下: 在c++中,单例模式的实现,较为常用的实现方式一般为: n ...
- python设计模式---创建型之工厂模式
# coding = utf-8 from abc import ABCMeta, abstractmethod # 简单工厂模式 class Animal(metaclass=ABCMeta): @ ...
- Java设计模式05:常用设计模式之原型模式(创建型模式)
1. Java之原型模式(Prototype Pattern) 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. ...
- 原型模式(Prototype)---创建型
1 基础知识 定义:原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.特征:不需要知道任何创建的细节,不调用构造方法.本质:克隆生成对象. 原型模式会要求对象实现一个可以“克隆”自身的接口 ...
随机推荐
- JS函数篇【2】
什么是函数 函数的作用,可以写一次代码,然后反复地重用这个代码. <h3 onload="add2(1,2,3);add3(4,5,6)"></h3> &l ...
- PowerMockito单元测试中的Invalid use of argument matchers问题详解
首先,简单说说PowerMockito进行单元测试的三部曲: 打桩,即为非测试目标方法设置返回值,这些返回值在测试目标方法中被使用.执行测试,调用测试目标方法.验证测试结果,如测试方法是否被执行,测试 ...
- JS创建SVG的问题
在线编辑的一个东西,用的是js+svg,遇到了这样一个问题,就是说我监听页面的单击事件,然后记录下来鼠标单击的位置,给svg添加子标签,然后页面上展示出来 说的可能不大清楚,上代码吧 <!DOC ...
- linux下环境管理anaconda3
我之前在centos之安装单独python3.6,大家都知道centos自带python2.7,通过输入python,和python3来控制想要使用python2,或者python3,如今想要要在li ...
- JDK源码那些事儿之LinkedBlockingQueue
今天继续讲解阻塞队列,涉及到了常用线程池的其中一个队列LinkedBlockingQueue,从类命名部分我们就可以看出其用意,队列中很多方法名是通用的,只是每个队列内部实现不同,毕竟实现的都是同一个 ...
- NoClassDefFoundError错误发生的原因
今上午项目怎么也起不来,总报这个错,上网查一下,大概解释如下:NoClassDefFoundError错误的发生,是因为Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类导致的错误.例如 ...
- Time 时间格式处理方法
一般时间调用都会精确到年 月 日 时 分 秒 怎么调用时去掉时 分 秒呢 用以下格式来处理 //时间格式处理 var time = new Date(data.FTime); var ...
- snmp_trap/snmptt
Zabbix Snmp Trap 配置 1. Zabbix Server 操作 1.1 Snmp Trap 安装配置 yum install -y net-snmp net-snmp-utils vi ...
- Kalman实际应用总结
目录 Kalman理论介绍 一. 简单理论介绍理论 二. 升华理论介绍 Kalman基本应用 一. Kalman跟踪/滤波 二. Kalman预测/融合(单传感器) 三. Kalman多传感器融合A ...
- centos7的redis加哨兵系统
三台服务器 1.下载 wget http://download.redis.io/releases/redis-5.0.3.tar.gztar -zxvf redis-5.0.3.tar.gzcd r ...