当前位置: 首页 > article >正文

Java创造型模式之原型模式详解

设计模式是面向对象设计中的一种标准方法,用于解决常见的设计问题。原型设计模式(Prototype Pattern)是23种经典设计模式之一,属于创建型模式,它允许通过复制现有对象来创建新对象,而不是通过构造函数或工厂方法来创建。这样,开发者可以在运行时通过复制原型对象来快速生成新的对象,极大地提高了程序的灵活性和性能。

本文将深入讲解Java中的原型设计模式,解释其概念、使用场景、以及如何在Java中实现。

一、原型设计模式的定义

原型模式是一种通过复制原型对象来创建新对象的设计模式。它使得对象的创建不依赖于具体的类构造,而是依赖于原型实例。原型实例通过浅拷贝或深拷贝的方式复制,从而生成新的实例对象。

关键点:

  1. 原型对象:一个可以复制的对象。
  2. 克隆操作:通过复制(克隆)原型对象来创建新的对象。
  3. 浅拷贝与深拷贝:浅拷贝指的是复制对象时,原对象和复制对象共享引用类型的成员变量。深拷贝则是完全复制对象,确保复制对象和原对象没有任何共享的引用类型变量。

二、使用原型模式的原因

在某些场景中,传统的对象创建方式可能过于复杂或不够高效。通过原型模式,我们可以通过现有的对象(即原型)来快速创建新对象,而无需重新构造对象。

原型模式的优势:

  1. 提高性能:当对象的创建过程比较复杂时,通过原型复制对象来创建新实例通常比使用构造函数更高效。
  2. 简化创建过程:对象的创建不需要重复复杂的初始化操作,只需要通过复制已有对象来实现。
  3. 支持变更:通过复制原型对象,开发者可以在运行时修改对象的某些属性,而不影响原对象。

适用场景:

  • 创建对象的过程较为复杂,且有多个相似对象需要频繁创建时,原型模式尤其有效。
  • 需要在程序运行时动态创建大量相似对象的情况。
  • 在复制对象时不希望重复调用构造函数,特别是当对象初始化代价较大时。

三、原型模式的实现

在Java中,原型模式通常通过实现Cloneable接口来实现,Cloneable接口是Java标准库中的一个标记接口,表示该对象支持克隆操作。Object类中的clone()方法是用于执行浅拷贝的默认实现。

1. 浅拷贝与深拷贝

  • 浅拷贝:复制对象时,只复制对象本身的基本数据类型成员,引用类型成员复制的是地址,意味着原对象和克隆对象会共享引用类型的成员。
  • 深拷贝:复制对象时,不仅复制对象本身,还会复制对象的引用类型成员,确保原对象和克隆对象互不影响。

2. 实现原型模式的步骤

步骤 1:实现 Cloneable 接口

首先,确保要复制的类实现了 Cloneable 接口。Cloneable接口是一个标记接口,它告诉Object.clone()方法该对象支持克隆操作。

步骤 2:重写 clone() 方法

由于Object类的clone()方法是保护的(protected),我们需要在自己的类中覆盖clone()方法。通常我们会将clone()方法设为public,以便外部可以调用。

步骤 3:深拷贝或浅拷贝

根据需求,可以在clone()方法中实现深拷贝或浅拷贝。默认的clone()方法是浅拷贝,如果需要深拷贝,需要手动实现。

四、Java中原型设计模式的示例代码

1、浅拷贝

// 实现Cloneable接口
class Prototype implements Cloneable {private String name;private int age;// 构造方法public Prototype(String name, int age) {this.name = name;this.age = age;}// 获取对象的浅拷贝@Overridepublic Prototype clone() {try {return (Prototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}// Getter和Setter方法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 "Prototype{" +"name='" + name + '\'' +", age=" + age +'}';}
}public class PrototypeDemo {public static void main(String[] args) {// 创建原型对象Prototype original = new Prototype("Alice", 30);System.out.println("Original Object: " + original);// 克隆原型对象Prototype clone = original.clone();System.out.println("Cloned Object: " + clone);// 修改克隆对象的属性clone.setName("Bob");clone.setAge(25);System.out.println("Modified Cloned Object: " + clone);System.out.println("Original Object after modification: " + original);}
}

结果为: 

Original Object: Prototype{name='Alice', age=30}
Cloned Object: Prototype{name='Alice', age=30}
Modified Cloned Object: Prototype{name='Bob', age=25}
Original Object after modification: Prototype{name='Alice', age=30}

2、深拷贝

class Address {private String street;private String city;public Address(String street, String city) {this.street = street;this.city = city;}public Address(Address address) {this.street = address.street;this.city = address.city;}@Overridepublic String toString() {return "Address{" +"street='" + street + '\'' +", city='" + city + '\'' +'}';}
}class DeepPrototype implements Cloneable {private String name;private Address address;public DeepPrototype(String name, Address address) {this.name = name;this.address = address;}@Overridepublic DeepPrototype clone() {try {DeepPrototype cloned = (DeepPrototype) super.clone();cloned.address = new Address(this.address); // 深拷贝return cloned;} catch (CloneNotSupportedException e) {e.printStackTrace();}return null;}@Overridepublic String toString() {return "DeepPrototype{" +"name='" + name + '\'' +", address=" + address +'}';}
}public class DeepPrototypeDemo {public static void main(String[] args) {Address address = new Address("Baker Street", "London");DeepPrototype original = new DeepPrototype("John", address);System.out.println("Original Object: " + original);// 深拷贝原型对象DeepPrototype cloned = original.clone();System.out.println("Cloned Object: " + cloned);// 修改克隆对象的属性cloned.address = new Address("Wall Street", "New York");System.out.println("Modified Cloned Object: " + cloned);System.out.println("Original Object after modification: " + original);}
}

结果为:

Original Object: DeepPrototype{name='John', address=Address{street='Baker Street', city='London'}}
Cloned Object: DeepPrototype{name='John', address=Address{street='Baker Street', city='London'}}
Modified Cloned Object: DeepPrototype{name='John', address=Address{street='Wall Street', city='New York'}}
Original Object after modification: DeepPrototype{name='John', address=Address{street='Baker Street', city='London'}}

五、总结

原型设计模式通过克隆现有对象来创建新对象,而不是每次都通过构造函数创建。这种方式非常适合需要频繁创建相似对象的场景。Java提供了Cloneable接口和clone()方法来支持该模式的实现。在实际开发中,使用原型模式可以减少对象创建时的性能开销,同时也可以在对象状态变化时避免重复操作。

无论是浅拷贝还是深拷贝,原型模式都能有效提高开发效率,并在某些情况下避免不必要的资源浪费。理解并合理使用原型模式,可以在复杂系统的设计中发挥重要作用。

相关文章:

Java创造型模式之原型模式详解

设计模式是面向对象设计中的一种标准方法,用于解决常见的设计问题。原型设计模式(Prototype Pattern)是23种经典设计模式之一,属于创建型模式,它允许通过复制现有对象来创建新对象,而不是通过构造函数或工厂…...

JVM的各种细节

(1)JVM 核心结构(必须知道) 类加载器 负责将.class()文件加载到内存中,供 JVM 使用。 方法区 存储类元数据(类名、字段、方法)、常量池、静态变量等。 JDK 8:由元空间(Metaspace)…...

JavaScript基本知识

文章目录 一、JavaScript基础1.变量(重点)1-1 定义变量及赋值1-2 变量的命名规则和命名规范判断数据类型: 2.数据类型转换2-1 其他数据类型转成数值2-2 其他数据类型转成字符串2-3 其他数据类型转成布尔 3.函数3-1函数定义阶段3-2函数调用阶段…...

Navicat for Snowflake 震撼首发,激活数据仓库管理全新动能

近日,Navicat 家族迎来了一位全新成员 — Navicat for Snowflake。Snowflake 是一款基于云架构的现代数据仓库解决方案,以其弹性扩展、高性能和易用性著称。这次首发的Navicat for Snowflake 专为简化 Snowflake 数据库管理任务而精心打造。它凭借其直观…...

pjsip 自定义获取和设置麦克风、扬声器

获取麦克风和扬声器列表结果Device ID: 0 Name: “Wave mapper” Input channels: 2 Output channels: 2 Default sample rate: 16000 Device ID: 1 Name: “麦克风 (USB Microphone)” Input channels: 2 Output channels: 0 Default sample rate: 16000 Device ID: 2 Name: “…...

C++ 左值(lvalue)和右值(rvalue)

在 C 中,左值(lvalue)和右值(rvalue)是指对象的不同类别,区分它们对于理解 C 中的表达式求值和资源管理非常重要,尤其在现代 C 中涉及到移动语义(Move Semantics)和完美转…...

深度学习基础:线性代数本质2——线性组合、张成的空间与基

目录 一、线性组合 1. 用一个有趣的角度看向量坐标 2. 如果我们选择不同的基向量会怎样? 3. 线性组合 4. 张成的空间 ① 二维向量的张成的空间 ② 三维向量的张成的空间​编辑 5.线性相关 6.线性无关 7. 基的定义 一、线性组合 1. 用一个有趣的角度看向量坐…...

第五天 Labview数据记录(5.4 EXCEL文件读写)

5.4 EXCEL文件读写 Excel 文件读写在数据处理、自动化办公、数据分析等领域具有重要的意义。以下是 Excel 文件读写的主要应用场景和意义:1. 数据管理和整理;2. 自动化办公;3. 数据分析和可视化;4. 系统集成;5. 报表生…...

iOS 模块化架构设计:主流方案与实现详解

随着 iOS 工程规模的扩大,模块化设计成为提升代码可维护性、团队协作效率和开发灵活性的关键。本文将探讨为什么需要模块化,介绍四种主流的模块化架构方案(协议抽象、依赖注入、路由机制和事件总线),并通过代码示例和对…...

【WRF模拟】如何查看 WPS 的输入静态地理数据(二进制格式)?

查看 WPS 的输入静态地理数据方法总结 方法 1:使用 gdal_translate 将二进制数据转换为 GeoTIFFgdal_translate 工具概述使用 gdal_translate 将二进制数据转换为 GeoTIFF方法 2:使用 ncdump 查看 geo_em.dXX.nc方法 3:使用 Python xarray + matplotlib 可视化 geo_em.dXX.n…...

麒麟系统利用pycharm生成deb文件

在麒麟系统(Kylin OS)上使用 PyCharm 进行 Python 开发并生成 .deb 可安装软件包,可以按照以下步骤进行操作: 1. 准备工作 安装 PyCharm:确保已经在麒麟系统上安装了 PyCharm,可以使用官方提供的安装包进…...

【日志队列】log日志实时写入队列,流式输出

有一个这样的任务:在网页上流式输出执行一个函数在终端产生的日志,但是目前只有终端日志,可以通过 自定义 loguru 的 Sink 将日志消息定向到线程安全的队列中,主线程从队列中实时获取日志。 import threading import queue from …...

注意力机制,层归一化,RBA。KAN-ODE,小波KAN

目录 attention is all you need 翻译 多头注意力 8.6 Multi-head Self Attention 模型 模型架构 encoder安定 decode 注意力机制 位置编码 自注意力机制的优势 实验结果 结论 代码 Transformer 架构 代码实现思路 总结 编码器、解码器和位置编码的摆放顺序&…...

【实战ES】实战 Elasticsearch:快速上手与深度实践-附录-3-从ES 7.x到8.x的平滑迁移策略

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 附录-版本升级指南 3-Elasticsearch 7.x 到 8.x 平滑迁移策略指南1. 升级必要性分析1.1 版本特性对比1.2 兼容性评估矩阵 2. 预升级准备清单2.1 环境检查表2.2 数据备份策略 3. 分阶段…...

go回调函数的使用

在Go语言中,回调函数可以有参数,也可以没有参数。它们的定义和使用方式略有不同,但本质上都是将函数作为参数传递给另一个函数,并在适当的时候调用它。以下是带参数和不带参数的回调函数的示例和说明。 1. 不带参数的回调函数 不…...

电脑内存不足怎么办?

常规解决方法盘点 关闭后台程序:按下【Ctrl Shift Esc】组合键打开任务管理器,在 “进程” 选项卡里,把当前不用的程序统统 “结束任务” ,像那些自动更新的软件、常驻后台的播放器,关了能释放不少内存。比如音乐软…...

如何上传文件到github

如何上传文件到github **方法 1:使用 Git 命令行(推荐)****步骤 1:初始化 Git 仓库(如果还没有)****步骤 2:添加远程仓库****步骤 3:添加整个文件夹并提交****步骤 4:推送…...

【RISCV LAB】0x01-安装实验仿真辅助工具

安装实验辅助工具 实验环境搭建安装 Verilator编译依赖下载源码编译安装测试安装 安装 RISC-V 交叉编译工具链编译依赖下载源码编译安装编译并安装添加环境变量并测试 安装 GTKWave其他模拟器推荐RARSemulsiV FAQ 实验环境搭建 Verilator 是一款开源的支持 Verilog 和 SystemV…...

Trae插件革命:用VSPlugin Helper实现VSCode市场插件全自动安装

之前有读者留言说trae都没有c的插件用,确实是这样,trae的插件源用的是open vsx,而c/c插件是vscode官方插件市场的,如果想直接在trae中安装c/c插件是不行的,只能先从vscode官方插件市场把vsix后缀文件先下载下来&#x…...

使用PHP进行自动化测试:工具与策略的全面分析

使用PHP进行自动化测试:工具与策略的全面分析 引言 随着软件开发的复杂性不断增加,自动化测试已成为确保软件质量的关键环节。PHP作为一种广泛使用的服务器端脚本语言,拥有丰富的生态系统和工具支持,使其成为自动化测试的理想选…...

Docker相关面试题

阅读前可以给我关注➕一下嘛 以下是150道Docker相关面试题: Docker基础概念 1.什么是Docker? Docker是一个开源的应用容器引擎,基于Go语言开发,遵循Apache2.0协议。它可以让开发者将应用及其依赖包打包成一个可移植的容器&#x…...

【量化策略】动量突破策略

【量化策略】动量突破策略 🚀量化软件开通 🚀量化实战教程 技术背景与应用场景 动量突破策略是一种基于市场趋势的量化交易策略,它通过识别和利用资产价格的持续上升或下降趋势来获取利润。这种策略特别适用于那些价格波动较大、趋势明显…...

RK3588 openssl-3.4.1 编译安装

安装依赖 sudo apt update && sudo apt install build-essential perl libtext-template-perl -y 下载并解压源码 wget https://www.openssl.org/source/openssl-3.4.1.tar.gz tar -xzf openssl-3.4.1.tar.gz && cd openssl-3.4.1 配置编译选项 ./config --…...

EB-Cable许可管理在云计算环境中的应用

随着云计算技术的飞速发展,越来越多的企业选择将业务迁移到云端,以享受其灵活性、可扩展性和成本效益。然而,在云计算环境中管理软件许可成为了一个新的挑战。幸运的是,EB-Cable许可管理以其卓越的功能和适应性,为企业…...

字符串函数和结构题内存对齐

图下为函数使用&#xff1a; #include <ctype.h>int main() {int ret isdigit(Q);printf("%d\n", ret);return 0; }int main() {printf("%c\n", toupper(a));printf("%c\n", tolower(A));return 0; }...

5.编译链接和宏**

1. 宏&#xff08;考察很多&#xff09;-要求轻松实现宏&#xff0c;很容易出错 #define 机制包括了一个规定&#xff0c;允许把参数替换到文本中&#xff0c;这种实现通常称为宏或定义宏。 下面是宏的声明方式&#xff1a; #define name(参数列表) 内容 参数列表的左括号必…...

《灵珠觉醒:从零到算法金仙的C++修炼》卷三·天劫试炼(43)阴阳镜照连通 - 岛屿数量(DFS、BFS)

《灵珠觉醒:从零到算法金仙的C++修炼》卷三天劫试炼(43)阴阳镜照连通 - 岛屿数量(DFS、BFS) 哪吒在数据修仙界中继续他的修炼之旅。这一次,他来到了一片神秘的阴阳镜湖,湖面上倒映着一片由二维网格构成的岛屿世界。湖边有一块巨大的石碑,上面刻着一行文字:“欲破此湖…...

Odoo18 Http鉴权+调用后端接口

最近在调研Odoo18&#xff0c;包括它的前后端原理、源码等。发现官方的开发文档并不十分实用&#xff0c;比如标题这种简单的实用需求&#xff0c;竟然浪费了一点时间&#xff0c;特此记录。 官方文档&#xff1a;External API — Odoo 18.0 documentation 前提&#xff1a;首…...

TSN CB:恢复算法与潜在错误检测

1.概述 基础恢复功能位于排序功能中。它评估从较低层向上传递的一个或多个成员流的数据包的sequence_number子参数&#xff0c;以丢弃重复的数据包。基本恢复功能的给定实例化可以作为序列恢复功能或单个恢复功能。 序列恢复功能有两种序列恢复算法&#xff0c;因此它既可用于间…...

【sql靶场】第11、12关-post提交注入

目录 【sql靶场】第11、12关-post提交注入 POST 一、URL 二、核心组成部分 三、数据编码规范 四、应用场景与请求方法 第十一关 方法一 步骤一 步骤二 步骤三 步骤四 步骤五 步骤六 步骤七 方法二 步骤一 步骤二 步骤三 步骤四 步骤五 步骤六 步骤七 第…...