【深入理解设计模式】原型设计模式

原型设计模式
原型设计模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来创建新对象,而无需直接依赖它们的具体类。这种模式通常用于需要频繁创建相似对象的场景,以避免昂贵的创建操作或初始化过程。
概述
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
结构
原型模式包含如下角色:
- 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
实现
原型模式的克隆分为浅克隆和深克隆。
(经典面试题)什么是深克隆和浅克隆?
答:在计算机内存中,每个对象都有一个地址,这个地址指向对象在内存中存储的位置。当我们使用变量引用一个对象时,实际上是将该对象的地址赋值给变量。因此,如果我们将一个对象复制到另一个变量中,实际上是将对象的地址复制到了这个变量中。
<-------------------------------------------------------------------------------------------------------------------->
浅拷贝是指将一个对象复制到另一个变量中,但是只是复制对象的地址,而不是对象本身。也就是说,原始对象的复制对象实际上是共享同一个内存地址的。因此,如果我们修改了复制对象中的属性或元素,原始对象中对应的属性或元素也会被修改。
<-------------------------------------------------------------------------------------------------------------------->
深拷贝是指将一个对象及其所有子对象都复制到另一个变量中,也就是说,它会创建一个全新的对象,并将原始对象中的所有属性或元素都复制到新的对象中。因此,如果我们修改复制对象中的属性或元素,原始对象中对应的属性或元素不会受到影响。
浅克隆:
- Java中的Object类中提供了
clone()方法来实现浅克隆。 Cloneable 接口是抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类。代码如下:
/*** @author OldGj * @version v1.0* @apiNote 具体原型类-原型模式*/
public class Realizetype implements Cloneable {public Realizetype() {System.out.println("原型创建");}@Overridepublic Realizetype clone() throws CloneNotSupportedException {System.out.println("原型克隆");return (Realizetype) super.clone();}
}
/*** @author OldGj * @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws CloneNotSupportedException {Realizetype realizetype = new Realizetype();Realizetype cloned = realizetype.clone();System.out.println(cloned==realizetype); // false}
}
- 使用BeanUtils实现浅拷贝
BeanUtils是 Apache Commons BeanUtils 库中的一个类,它提供了一组用于操作 Java Bean 对象的工具方法。Java Bean 是一种符合特定约定的 Java 类,通常包含私有字段、公共 getter 和 setter 方法。BeanUtils 库可以用于在不直接访问对象字段的情况下操作 Bean 对象的属性。
/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 使用BeanUtils实现浅克隆 - 用户类*/
public class User {private String name;private String password;private Address address;--- 省略get/set方法
}
/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 使用BeanUtils实现浅拷贝 - 地址类*/
public class Address {private String province;private String city;--- 省略get/set方法
}
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws CloneNotSupportedException, InvocationTargetException, IllegalAccessException {User user = new User();user.setAddress(new Address("beijing","shanghai"));User user1 = new User();BeanUtils.copyProperties(user,user1);System.out.println(user == user1); // falseSystem.out.println(user.getAddress() == user1.getAddress()); // true : 浅拷贝}
}
深克隆:
- 实现Cloneable接口,重写clone();
在Objecta类中定义了一个clone方法,这个方法其实在不重写的情况下,其实也是浅拷贝的。
如果想要实现深拷贝,就需要重写clone方法,而想要重写clone方法,就必须实现Cloneable,否则会报Clone NotSupportedException异常。
/*** @author OldGj 2024/02/23* @version v1.0* @apiNote 重写clone方法实现深克隆- 地址类*/
public class Address {private String province;private String city;--- 省略get/set方法
}
/*** @author OldGj * @version v1.0* @apiNote 重写clone方法实现深克隆 - 用户类*/
public class User implements Cloneable{private String name;private String password;private Address address;@Overrideprotected Object clone() throws CloneNotSupportedException {User user = (User) super.clone();user.setAddress(new Address());return user;}--- 省略get/set方法
}
/*** @author OldGj * @version v1.0* @apiNote 测试类 - 测试原型模式克隆*/
public class Client {public static void main(String[] args) throws Exception {User user = new User();User clone = (User) user.clone();System.out.println(user == clone); // falseSystem.out.println(user.getAddress() == clone.getAddress()); // false 深克隆}
}
这种方式的逻辑很好理解,就是在重写的clone()方法中,将对象的所有引用类型的属性全部设置为一个新实例化的引用即可。
但是采用这种方式也有一个缺点就是,如果我们User类中引用类型的属性非常多,那么clone()方法需要写很长,而且后面如果有修改,比如在User类中新增了属性,那么这个地方也要修改,并且通过这种方式实现深克隆后,我们也无法再调用上述浅克隆的方式进行浅克隆了。
- 序列化实现深克隆:我们可以借助序列化来实现深拷贝。先把对象序列化成流,再从流中反序列化成对象,这样就一定是新的对象了。
序列化的方式有很多,比如我们可以使用各种JSON工具,把对象序列化成SON字符串,然后再从字符串中反序列化成对象。
使用fastjson工具包序列化实现深拷贝:
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 奖状类*/
public class Citation implements Cloneable, Serializable {private Student student;public void show() {System.out.println(student.getName() + "获得了奖状");}public Student getStudent() {return student;}public void setStudent(Student student) {this.student = student;}@Overrideprotected Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}
}
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 学生类*/
public class Student implements Serializable {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
/*** @author OldGj * @version v1.0* @apiNote 客户端 - 测试深拷贝 - 使用fastjson工具包实现序列化*/
public class CitationTest {public static void main(String[] args) throws Exception {Citation citation = new Citation();citation.setStudent(new Student());String jsonString = JSON.toJSONString(citation);Citation newCitation = JSON.parseObject(jsonString, Citation.class);System.out.println(citation == newCitation); // false System.out.println(citation.getStudent() == newCitation.getStudent()); // false 深拷贝}
}
使用SerializationUtils工具类实现深拷贝:
SerializationUtils 是 Apache Commons Lang 库中的一个工具类,用于实现 Java 对象的序列化和反序列化。它提供了一组静态方法,可以方便地将对象序列化为字节数组,或者将字节数组反序列化为对象。
引入Apache Commons Lang库依赖:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version> <!-- 或者是当前最新版本 -->
</dependency>
/*** @author OldGj 2024/02/21* @version v1.0* @apiNote 客户端 - 测试类 测试深拷贝*/
public class CitationTest2 {public static void main(String[] args) throws CloneNotSupportedException {Citation citation = new Citation();Student student = new Student();student.setName("张三");citation.setStudent(student);Citation cloned = SerializationUtils.clone(citation); //基于序列化实现深拷贝System.out.println(citation==cloned); // falseSystem.out.println(citation.getStudent()==cloned.getStudent()); // false 深克隆Student student1 = new Student();student1.setName("李四");cloned.setStudent(student1); // 修改克隆对象的引用类型属性,原对象的该属性不变System.out.println(citation.getStudent().getName());System.out.println(cloned.getStudent().getName());}
}
注意事项
序列化版本一致性:确保序列化和反序列化的类具有相同的 serialVersionUID,以避免反序列化时出现 InvalidClassException。
Serializable 接口:要序列化一个对象,该对象的类必须实现 Serializable 接口。
SerializationUtils 提供了一种简单而强大的方式来实现对象的序列化和反序列化,以及对象的深度复制,从而简化了 Java 应用程序中的对象操作。Citation类和Student类必须实现Serializable接口,否则会抛NotSerializableException异常。
相关文章:
【深入理解设计模式】原型设计模式
原型设计模式 原型设计模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制已有对象来创建新对象,而无需直接依赖它们的具体类。这种模式通常用于需要频繁创建相似对象的场景,以避免昂贵的创建操作或初始化过…...
Python算法题集_图论(课程表)
Python算法题集_课程表 题207:课程表1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【循环递归全算】2) 改进版一【循环递归缓存】3) 改进版二【循环递归缓存反向计算】4) 改进版三【迭代剥离计数器检测】 4. 最优算法5. 相关资源 本…...
视频评论挖掘软件|抖音视频下载工具
针对抖音视频下载的需求,我们开发了一款功能强大的工具,旨在解决用户在获取抖音视频时需要逐个复制链接、下载的繁琐问题。我们希望用户能够通过简单的关键词搜索,实现自动批量抓取视频,并根据需要进行选择性批量下载。因此&#…...
Linux学习方法-框架学习法——Linux驱动架构的演进
配套视频学习链接:https://www.bilibili.com/video/BV1HE411w7by?p4&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux驱动演进的过程 Linux驱动的原始架构(Linux V2.4) 平台总线架构(platform) Linux设备树 Linux驱动演进的趋势 Linux驱动演进的过程…...
Spring Boot基础面试问题(一)
上篇文章中10个Spring Boot面试问题的标准答案: 什么是Spring Boot?它与Spring框架有什么区别? 标准回答:Spring Boot是基于Spring框架的快速开发框架,它简化了Spring应用程序的搭建和配置过程,提供了一套自…...
电路设计(28)——交通灯控制器的multisim仿真
1.功能设定 南北、东西两道的红灯时间、绿灯时间均为24S,数码管显示倒计时。在绿灯的最后5S内,黄灯闪烁。有夜间模式:按下按键进入夜间模式。在夜间模式下,数码管显示计数最大值,两个方向的黄灯不停闪烁。 2.电路设计 …...
【Docker】免费使用的腾讯云容器镜像服务
需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。 目录 1、设置密码 2、登录实例(sudo docker login xxxxxx) 3、新建命名空间(每个命名空…...
如何让qml使用opengl es
要让 QML 使用 OpenGL ES,您需要确保项目配置正确,并在应用程序中使用 QSurfaceFormat 来设置 OpenGL ES 渲染。 以下是一些步骤来配置 QML 使用 OpenGL ES: 1、项目配置:在您的项目配置文件(例如 .pro 文件…...
金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了!!
金航标电子位于广西柳州鹿寨县天线生产基地于大年正月初九开工了!!!金航标kinghelm( http://www.kinghelm.com.cn )总部位于中国深圳市,兼顾技术、成本、管理、效率和可持续发展。东莞塘厦实验室全电波暗…...
FlinkCDC详解
1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture(数据变更捕获)的简称。其核心原理就是监测并捕获数据库的变动(例如增删改),将这些变更按照发生顺序捕获,将捕获到的数据,写入数据…...
力扣代码学习日记六
Problem: 66. 加一 思路 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输…...
「Python系列」Python标准库
文章目录 一、 os 模块:文件和目录操作二、 sys 模块:与Python解释器交互三、 datetime 模块:日期和时间处理四、 json 模块:处理JSON数据五、 re 模块:正则表达式六、 time模块1. 获取当前时间2. 延迟执行(…...
虚拟列表【vue】等高虚拟列表/非等高虚拟列表
文章目录 1、等高虚拟列表2、非等高虚拟列表 1、等高虚拟列表 参考文章1 参考文章2 <!-- eslint-disable vue/multi-word-component-names --> <template><divclass"waterfall-wrapper"ref"waterfallWrapperRef"scroll"handleScro…...
【MySQL】如何理解索引(高频面试点)
一、前言 首先这个博客会介绍一些关于MySQL中索引的基本内容以及一些基本的语法,当然里面也会有些常见的面试题的解答。 二、关于索引 1、概念 索引是一种能够帮助MySQL高效的去磁盘检索数据的一种数据结构。在MySQL的Innodb存储引擎中呢,采用的是B树的…...
NXP实战笔记(四):S32K3xx如何产生中心对称三相六路波形
目录 1、概述 1.1、理论基础 2、RTD实现 2.1、Emios时基配置 2.1.1、EmiosMcl 2.1.2、EmiosCommon 2.2、Emios PWM配置 2.3、TRGMUX 2.4、LCU 2.5、外设信号配置 3、代码实现 4、测试结果 1、概述 电机控制中需要产生三相六路SVPWM进行占空比与周期调制,怎么通过RT…...
关于uniapp H5应用无法在触摸屏正常显示的处理办法
关于uniapp H5应用无法在触摸屏正常显示的处理办法 1、问题2、处理3、建议 1、问题 前几天, 客户反馈在安卓触摸大屏上无法正确打开web系统(uni-app vue3开发的h5 应用),有些页面显示不出内容。该应用在 pc 端和手机端都可以正常…...
Stable Diffusion 3 发布,AI生图效果,再次到达全新里程碑!
AI生图效果,再次到达全新里程碑! Prompt:Epic anime artwork of a wizard atop a mountain at night casting a cosmic spell into the dark sky that says "Stable Diffusion 3" made out of colorful energy 提示(意译…...
单例模式怎样实现单例(独例)?
在类定义中加入私有属性 __init__flag Ture,在随后的初始化处理中,判断该属性为真时进行相应的初始化操作,否则,跳过相应的初始化操作。这个机制,保证在进行后续的调用时,不再占用额外的内存开销。 当然了,…...
MySQL——基础内容
目录 第01章_数据库概述 关系型数据库(RDBMS)——表、关系模型 非关系型数据库(非RDBMS) 表、记录、字段 表的关联关系 一对一关联 一对多关系 多对多 自我引用 第02章_MySQL环境搭建 登录命令 常用命令 show databases; create database use 数据库名 show tables 第03章…...
node 之 初步认识
思考:为什么JavaScript可以在浏览器中被执行 代执行的js代码——JavaScript解析引擎 不同的浏览器使用不同的JavaScript解析引擎 Chrome 浏览器 》 V8 Firefox浏览器 》OdinMonkey(奥丁猴) Safri浏览器 》JSCore IE浏览器 》Chakra(查克拉) e…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
