三、原型模式
一、什么是原型模式
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。原型模式包含以下主要角色。
- 抽象原型类:规定了具体原型对象必须实现的接口。(Cloneable)
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。(Prototype)
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。(Main.class)
二、原型模式的实现
深克隆与与浅克隆:Object类的clone方法只会克隆对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会克隆,这就是浅克隆。如果要实现深克隆,必须将原型模式中的数组、容器对象、引用对象等另行克隆。
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
1、创建Prototype的成员属性:
/*** @author FluffyCatkin* @version 1.0* @date 2021-03-31 0:12* @description Prototype的成员变量*/
public class Member implements Cloneable{private String position;public Member(String position) {this.position = position;}public void setPosition(String position) {this.position = position;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Member{" +"position='" + position + '\'' +'}';}
}
2、创建具体原型类Prototype:
import java.util.ArrayList;public class Prototype implements Cloneable{private String name ;private int age;private ArrayList<String> hobbies;private Member member;public Prototype(String name, int age, ArrayList<String> hobbies, Member member) {this.name = name;this.age = age;this.hobbies = hobbies;this.member = member;System.out.println("通过构造方法创建对象。。。。。。");}/*** 浅克隆*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}/*** 深克隆*/
// @Override
// protected Object clone() throws CloneNotSupportedException {
// Prototype clone = (Prototype) super.clone();
// clone.setHobbies((ArrayList<String>) clone.getHobbies().clone());
// clone.setMember((Member) clone.getMember().clone());
// return clone;
// }public void setName(String name){this.name = name;}public void setHobbies(ArrayList<String> hobbies) {this.hobbies = hobbies;}public ArrayList<String> getHobbies() {return hobbies;}public Member getMember() {return member;}public void setMember(Member member) {this.member = member;}@Overridepublic String toString() {return "Prototype{" +"name='" + name + '\'' +", age=" + age +", hobbies=" + hobbies +", member=" + member +'}';}
}
3、创建测试类(访问类):
import java.util.ArrayList;/*** 原型模式* 原型模式的定义与特点:* 原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,* 原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,* 如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。* 原型模式的结构与实现由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。* 模式的结构* 原型模式包含以下主要角色。* 抽象原型类:规定了具体原型对象必须实现的接口。 (Cloneable)* 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。 (Prototype)* 访问类:使用具体原型类中的 clone() 方法来复制新的对象。 (Main.class)*/
public class Main {@Testpublic void prototypeTest() throws CloneNotSupportedException {Member initMember = new Member("c#");ArrayList<String> initHobbies = new ArrayList<>();initHobbies.add("play");Prototype prototype = new Prototype("张三",18,initHobbies,initMember);System.out.println("复制前");System.out.println("被克隆对象:"+prototype);Prototype prototype1 = (Prototype) prototype.clone();System.out.println("复制后");System.out.println("是否同一对象:"+(prototype==prototype1?"是":"否"));System.out.println("修改被克隆对象属性:");prototype.setName("李四");initHobbies.add("eat");initMember.setPosition("java");System.out.println("被克隆对象:"+prototype);System.out.println("克隆出的对象:"+prototype1);}}
- 当把深克隆的实现方法注释,使用浅克隆的方法时:

运行结果:
通过构造方法创建对象。。。。。。
复制前
被克隆对象:Prototype{name='张三', age=18, hobbies=[play], member=Member{position='c#'}}
复制后
是否同一对象:否
修改被克隆对象属性:
被克隆对象:Prototype{name='李四', age=18, hobbies=[play, eat], member=Member{position='java'}}
克隆出的对象:Prototype{name='张三', age=18, hobbies=[play, eat], member=Member{position='java'}}
当修改被克隆对象hobbies与member属性的时候,克隆出来对象的hobbies与member属性也被修改,可见这两个属性都是同一对象引用,而String类型的name以及基础类型的age属性是不会被同时修改的,可见不是同一引用。浅克隆只复制对象的String类型属性以及一些基本类型属性,是不完全克隆。
- 当把浅克隆的实现方法注释,使用深克隆的方法时:

运行结果:
通过构造方法创建对象。。。。。。
复制前
被克隆对象:Prototype{name='张三', age=18, hobbies=[play], member=Member{position='c#'}}
复制后
是否同一对象:否
修改被克隆对象属性:
被克隆对象:Prototype{name='李四', age=18, hobbies=[play, eat], member=Member{position='java'}}
克隆出的对象:Prototype{name='张三', age=18, hobbies=[play], member=Member{position='c#'}}
当修改被克隆对象name、age、hobbies以及member属性的时候,克隆出来对象的hobbies与member属性并未被修改,可见通过这种深克隆的方法,把所有的属性都创建一个新的内存对象,并使被克隆对象与克隆出的对象所有属性有不同的地址引用,深克隆的复制更加彻底。
注意:上面代码在执行克隆的时候并未打印构造方法中的:“通过构造方法创建对象。。。。。。”,因此可见,使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
三、应用场景
原型模式通常适用于以下场景。
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
四、优缺点分析
原型模式的优点:
- Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
原型模式的缺点:
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
代码地址:https://gitee.com/fluffycatkin/JavaDesignModel.git

原文出处:
https://blog.csdn.net/zhengzhb/article/details/7393528
http://c.biancheng.net/view/1343.html
相关文章:
三、原型模式
一、什么是原型模式 原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效&a…...
transformer实现词性标注
1、self-attention 1.1、self-attention结构图 上图是 Self-Attention 的结构,在计算的时候需要用到矩阵 Q(查询), K(键值), V(值)。在实际中,Self-Attention 接收的是输入(单词的表示向量 x组成的矩阵 X) 或者上一个 Encoder block 的输出。而 Q, K, V…...
Java中异或操作和OTP算法
最近在研究加密算法,发现异或操作在加密算法中用途特别广,也特别好用。下面以Java语言为例,简单记录一下异或操作,以及在算法中的使用,包括常用的OTP算法。 一,异或操作特征 1, 相同出0&#…...
K8S最新版本集群部署(v1.28) + 容器引擎Docker部署(下)
温故知新 📚第三章 Kubernetes各组件部署📗安装kubectl(可直接跳转到安装kubeadm章节,直接全部安装了)📕下载kubectl安装包📕执行kubectl安装📕验证kubectl 📗安装kubead…...
女子垒球运动的发展·垒球1号位
女子垒球运动的发展 1. 女子垒球运动的起源和发展概述 女子垒球运动,诞生于19世纪末的美国,作为棒球运动的衍生品,经过百年的积淀,已在全球范围内广泛传播,形成了丰富的赛事文化。她的起源,可以追溯到19世…...
Debian 30 周年,生日快乐!
导读近日是 Debian 日,也是由伊恩-默多克(Ian Murdock)创立的 Debian GNU/Linux 通用操作系统和社区支持的 Debian 项目 30 周年纪念日。 不管你信不信,从已故的伊恩-默多克于 1993 年 8 月 16 日宣布成立 Debian 项目,…...
字符串匹配的Rabin–Karp算法
leetcode-28 实现strStr() 更熟悉的字符串匹配算法可能是KMP算法, 但在Golang中,使用的是Rabin–Karp算法 一般中文译作 拉宾-卡普算法,由迈克尔拉宾与理查德卡普于1987年提出 “ 要在一段文本中找出单个模式串的一个匹配,此算法具有线性时间的平均复杂度࿰…...
傅里叶变换(FFT)笔记存档
参考博客:https://www.luogu.com.cn/blog/command-block/fft-xue-xi-bi-ji 目录: FFT引入复数相关知识单位根及其相关性质DFT过程(难点)DFT结论(重要)IDFT结论(重要)IDFT结论证明&…...
ELK安装、部署、调试 (二) ES的安装部署
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口操作ES,也可以利用Java API。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业…...
Android 13 - Media框架(8)- MediaExtractor
上一篇我们了解了 GenericSource 需要依赖 IMediaExtractor 完成 demux 工作,这一篇我们就来学习 android media 框架中的第二个服务 media.extractor,看看 IMediaExtractor 是如何创建与工作的。 1、MediaExtractorService media.extractor 和 media.p…...
Flutter 混合开发调试
针对Flutter开发的同学来说,大部分的应用还是Native Flutter的混合开发,所以每次改完Flutter代码,运行整个项目无疑是很费时间的。所以Flutter官方也给我们提供了混合调试的方案【在混合开发模式下进行调试】,这里以Android Stud…...
C语言每日一练------(Day3)
本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今天练习题的关键字: 尼科彻斯定理 等差数列 💓博主csdn个人主页:…...
14、监测数据采集物联网应用开发步骤(10)
监测数据采集物联网应用开发步骤(9.2) Modbus rtu协议开发 本章节在《监测数据采集物联网应用开发步骤(7)》基础上实现可参考《...开发步骤(7)》调试工具,本章节代码需要调用modbus_tk组件,阅读本章节前建议baidu熟悉modbus rtu协议内容 组件安装modb…...
Linux禅道上修改Apache 和 MySQL 默认端口号
1. 修改Apache默认端口号 80 cd /opt/zbox/etc/apachevim httpd.conf :wq 保存 2. 修改MySQL默认端口号 3306 cd /opt/zbox/etc/mysql vim my.cnf :wq 保存 3. 重启服务 ./zbox restart...
操作教程|通过1Panel开源Linux面板快速安装DataEase
DataEase开源数据可视化分析工具(dataease.io)的在线安装是通过在服务器命令行执行Linux命令来进行的。但是在实际的安装部署过程中,很多数据分析师或者业务人员经常会因为不熟悉Linux操作系统及命令行操作方式,在安装DataEase的过…...
机器学习策略——优化深度学习系统
正交化(Orthogonalization) 老式电视机,有很多旋钮可以用来调整图像的各种性质,对于这些旧式电视,可能有一个旋钮用来调图像垂直方向的高度,另外有一个旋钮用来调图像宽度,也许还有一个旋钮用来…...
ES6中Proxy和Proxy实例
1.Proxy Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器” 使用方法 let p new Proxy(target, handler);其中,target 为被代理对象。handler 是一个对象,其声明了代理 target 的一些操作。p 是…...
UDP协议的重要知识点
UDP,即用户数据报协议(User Datagram Protocol),是一个简单的无连接的传输层协议。与TCP相比,UDP提供了更少的错误检查机制,并允许数据包在网络上更快地传输。在这篇博客中,我们将深入探讨UDP的…...
QT6为工程添加资源文件,并在ui界面引用
以添加图片资源为例 右键工程名字(不是最上面的名字),点击添加现有文件 这种方式虽然添加到了工程中,但不能在UI设计界面完成引用。主要原因可能是未把文件放入到项目资源文件中,以下面一种方式可以看出区别。 点击添…...
Python小知识 - 如何使用Python的Flask框架快速开发Web应用
如何使用Python的Flask框架快速开发Web应用 现在越来越多的人把Python作为自己的第一语言来学习,Python的简洁易学的语法以及丰富的第三方库让人们越来越喜欢上了这门语言。本文将介绍如何使用Python的Flask框架快速开发Web应用。 Flask是一个使用Python编写的轻量级…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
