Java 与设计模式(12):享元模式
一、定义
享元模式是一种结构型设计模式,旨在有效地共享对象以减少内存使用和提高性能。该模式的核心思想是通过共享尽可能多的相似对象来减少内存占用。它将对象分为可共享的内部状态和不可共享的外部状态。内部状态是对象的固有属性,可以在多个对象之间共享,而外部状态是对象的上下文相关属性,每个对象都是独立的。
通过共享内部状态,享元模式可以减少创建相似对象的数量,从而节省内存空间。当需要创建新对象时,可以首先检查是否已经存在具有相同内部状态的对象,如果存在,则可以直接返回共享的对象,而不是创建新的对象。这种共享对象的方式可以在大规模使用相似对象的场景中提高性能和效率。
需要注意的是,享元模式需要维护一个对象池或缓存来存储共享的对象,以便在需要时进行检索。同时,外部状态的管理也需要谨慎处理,确保每个对象在不同的上下文中都能正确地使用。
享元模式通过共享相似对象的内部状态来减少内存占用和提高性能,是一种常用的优化技术。
二、Java示例
import java.util.HashMap;
import java.util.Map;// 具体享元类
class ConcreteFlyweight {private String intrinsicState;public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}public void operation(String extrinsicState) {System.out.println("Intrinsic State: " + intrinsicState);System.out.println("Extrinsic State: " + extrinsicState);}
}// 享元工厂类
class FlyweightFactory {private Map<String, ConcreteFlyweight> flyweights = new HashMap<>();public ConcreteFlyweight getFlyweight(String key) {if (flyweights.containsKey(key)) {return flyweights.get(key);} else {ConcreteFlyweight flyweight = new ConcreteFlyweight(key);flyweights.put(key, flyweight);return flyweight;}}
}// 客户端代码
public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();ConcreteFlyweight flyweight1 = factory.getFlyweight("shared");flyweight1.operation("state 1");ConcreteFlyweight flyweight2 = factory.getFlyweight("shared");flyweight2.operation("state 2");ConcreteFlyweight flyweight3 = factory.getFlyweight("unique");flyweight3.operation("state 3");}
}
在上述示例中,ConcreteFlyweight
类表示具体的享元对象,包含一个内部状态intrinsicState
。FlyweightFactory
类作为享元工厂,维护一个对象池flyweights
,用于存储共享的享元对象。
客户端代码通过FlyweightFactory
获取享元对象,并调用其operation
方法进行操作。当请求的享元对象已存在于对象池中时,直接返回共享的对象;否则,创建新的享元对象并添加到对象池中。
这样,通过共享相似对象的内部状态,可以减少创建对象的数量,节省内存空间。在示例中,flyweight1
和flyweight2
共享了相同的内部状态,而flyweight3
是一个独立的对象。
三、优点
享元模式的优点包括:
-
减少内存占用:通过共享相似对象的内部状态,可以减少创建对象的数量,从而减少内存占用。
-
提高性能:由于减少了对象的数量,可以减少对象的创建和销毁过程,从而提高程序的性能。
-
提高对象复用性:通过共享对象,可以在不同的上下文中复用对象,避免重复创建相似的对象。
-
简化对象管理:享元模式将对象的内部状态和外部状态进行分离,使得对象的管理更加简单和清晰。
-
支持大规模对象共享:当系统中存在大量相似对象时,通过享元模式可以有效地管理和共享这些对象,提高系统的可扩展性和可维护性。
享元模式通过共享相似对象的内部状态,可以减少内存占用、提高性能和对象复用性,简化对象管理,并支持大规模对象共享。这使得享元模式成为一种有价值的优化技术。
四、缺点
享元模式的缺点包括:
-
对象共享可能导致线程安全问题:如果多个线程同时访问共享对象,并且修改了对象的外部状态,可能会导致线程安全问题。需要在使用享元对象时进行适当的同步控制。
-
对象池管理增加复杂性:享元模式需要维护一个对象池或缓存来存储共享对象,这增加了对象管理的复杂性。需要确保正确地管理对象的创建、共享和销毁,避免资源泄露或过度消耗。
-
对象共享可能降低系统灵活性:当对象的内部状态和外部状态耦合较高时,共享对象可能限制了系统的灵活性。如果需要修改共享对象的内部状态,可能会影响到其他共享该对象的地方。
-
需要额外的内部状态管理:享元模式将对象的内部状态和外部状态进行分离,需要额外的管理和维护内部状态的机制。这增加了一定的复杂性和开销。
享元模式在提高性能和减少内存占用方面有优势,但也需要考虑线程安全、对象管理复杂性以及灵活性等方面的问题。在使用时需要根据具体场景进行权衡和设计。
五、使用场景
享元模式适用于以下场景:
-
对象数量庞大且相似:当系统中存在大量相似的对象,并且这些对象可以共享部分或全部内部状态时,可以使用享元模式来减少对象的数量和内存占用。
-
对象的外部状态可分离:对象的外部状态可以被分离出来,并且可以通过参数传递给对象的方法。这样可以将对象的内部状态与外部状态分离,使得对象可以共享内部状态。
-
需要缓存或池化对象:如果需要频繁地创建和销毁对象,并且对象的创建和销毁过程较为耗时,可以使用享元模式来缓存或池化对象,提高性能。
-
系统需要支持大规模对象共享:当系统中存在大量相似对象,并且这些对象需要在不同的上下文中共享时,可以使用享元模式来管理和共享这些对象,提高系统的可扩展性和可维护性。
需要注意的是,使用享元模式需要权衡对象共享带来的线程安全问题、对象管理的复杂性以及灵活性的影响。在具体应用时,需要根据实际情况进行设计和优化。
六、注意事项
在使用享元模式时,需要注意以下几点:
-
线程安全性:如果多个线程同时访问共享对象,并且修改了对象的外部状态,可能会导致线程安全问题。需要在使用享元对象时进行适当的同步控制,确保线程安全。
-
对象池管理:享元模式需要维护一个对象池或缓存来存储共享对象,需要确保正确地管理对象的创建、共享和销毁。需要注意避免资源泄露或过度消耗。
-
内部状态和外部状态的划分:需要合理划分对象的内部状态和外部状态,确保内部状态可以共享,而外部状态可以通过参数传递给对象的方法。同时,需要注意内部状态和外部状态的耦合度,避免影响系统的灵活性。
-
对象的可变性:享元模式适用于对象的内部状态是不可变的情况。如果对象的内部状态是可变的,需要考虑如何处理共享对象的可变性,以及如何保证共享对象的一致性。
-
性能权衡:使用享元模式可以提高性能和减少内存占用,但也需要权衡对象共享带来的管理复杂性和灵活性的影响。在具体应用时,需要根据实际情况进行设计和优化。
使用享元模式需要综合考虑线程安全性、对象管理、内部状态和外部状态的划分、对象的可变性以及性能权衡等方面的问题。在具体应用时,需要根据实际需求和场景进行适当的设计和调整。
七、在spring 中的应用
在Spring源码中,享元模式被广泛应用于各个模块和组件中,以提高性能和减少内存占用。以下是一些Spring源码中使用享元模式的示例:
-
Bean对象的管理:在Spring的IoC容器中,Bean对象被视为享元对象。当容器启动时,会预先创建并缓存Bean对象,以便在需要时进行共享和复用。这样可以减少对象的创建和销毁开销,提高性能。
-
数据库连接池:Spring的JDBC模块中,使用享元模式管理数据库连接。连接池中的连接对象被视为享元对象,可以在多个线程之间共享和复用,避免频繁地创建和销毁连接,提高数据库操作的性能。
-
缓存管理:Spring的缓存模块中,使用享元模式管理缓存对象。缓存对象被视为享元对象,可以在多个请求之间共享和复用,避免重复计算和查询,提高系统的响应速度。
-
国际化资源管理:Spring的国际化模块中,使用享元模式管理国际化资源对象。国际化资源对象被视为享元对象,可以在多个地方共享和复用,避免重复加载和解析资源文件,提高国际化功能的性能。
需要注意的是,Spring框架中对享元模式的应用往往是隐式的,封装在各个模块和组件中,不会直接暴露给开发者。这样可以提供更加简洁和易用的API,同时隐藏了底层的实现细节。
相关文章:
Java 与设计模式(12):享元模式
一、定义 享元模式是一种结构型设计模式,旨在有效地共享对象以减少内存使用和提高性能。该模式的核心思想是通过共享尽可能多的相似对象来减少内存占用。它将对象分为可共享的内部状态和不可共享的外部状态。内部状态是对象的固有属性,可以在多个对象之…...
React配置代理(proxy)
使用axios进行请求,而配置代理过程。 第一种 在package.json中,添加proxy配置项,之后所有的请求都会指向该地址 但这种方法只能配置一次,也只有一个 示例: "proxy":"https://localhost:5000" 添加后&am…...

队列(Queue):先进先出的数据结构队列
栈与队列https://blog.csdn.net/qq_45467165/article/details/127958960?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127958960%22%2C%22source%22%3A%22qq_45467165%22%7D 队列(Queue)是一种常见的线…...

CentOS ens160 显示disconnected
使用nmcli device查看网卡状态,显示如图: 检查宿主机系统VMware DHCP Sevice和VMware NAT Sevice服务是否正常运行。 右键点击我的电脑管理按钮,打开计算机管理点击服务...

使用 ChatGPT 创建 PowerPoint 演示文稿
让 ChatGPT 成为您的助手来帮助您编写电子邮件很简单,因为众所周知,它非常能够生成文本。很明显,ChatGPT 无法帮助您做饭。但您可能想知道它是否可以生成文本以外的其他内容。在上一篇文章中,您了解到 ChatGPT 只能通过中间语言为您生成图形。在这篇文章中,您将了解使用中…...

matlab将数组值划分为两类
例如:大于0的处理为1,小于0的处理为-1. 当然,可以选择循环结构和选择结构,但是效率会很低。 这里直接使用逻辑语句完成。 % 不使用循环语句,将数组内值划分为两类 clc; clearvars; a[-0.1422 , -0.0433 , 0.1131 …...

【点击新增一个下拉框 与前一个内容一样 但不能选同一个值】
点击新增一个下拉框 与前一个内容一样 但不能选同一个值 主要是看下拉选择el-option的disabled,注意不要混淆 <el-form label-width"120px" :model"form" ref"form" style"color: #fff"><template v-for"(trapolicy, i…...

【Gitee提交pr】
Gitee提交pr 什么是pr怎样提交一个pr嘞? 什么是pr pr:指的是将自己的修改从自己的账号仓库dev下提交到官方账号仓库master下; 通俗来讲就是Gitee线上有属于自己的分支,然后本地在自己地分支修改完代码之后,提交到自己的线上分支&a…...

一款打工人必备的电脑端自律软件!!冲鸭打工人!!
你!有没有渴望进步!! 你!有没有渴望变强!!! 成为大佬!!!超越巨佬!!! 这就是一款为这样的你量身定做的程序:输入…...

【Vue框架】 router和route是什么关系
前言 之前没太注意,写着写着突然发现它们貌似不太一样,记录以下,回顾的看总结就好。 1、总结✨ route:当前激活路由的对象,用于访问和操作当前路由的信息 router:管理多个route的对象,整个应…...

整理mongodb文档:聚合管道
个人博客 整理mongodb文档:聚合管道 个人博客,求关注,电脑版看体验更加,如果不够清晰,请指出来,谢谢 文章概叙 文章主要通过几个常用的聚合表达式来介绍聚合管道的使用,以及从索引的角度来介绍聚合管道…...

Delphi 11.3 FMX 多设备平台中使用 TGrid 实现类似 TDBGrid 的效果
Delphi Firemonkey 中 TDBGrid 这个控件已经没有了。如何实现类似这个效果呢。其实可以用TGrid 来实现。以下用 11.3 来讲解。 查询里面用到的 connection 和 query 等控件那些一般的数据库用法,就不做过多描述了。请参考其他资料。 方法一.通过界面配置来实现 在…...
Qt-事件循环与QtConcurrent、QThread结合使用时注意的点
QEventLoop和QtConcurrent可以结合使用达到主线程ui不阻塞同步执行的效果,但是要小心避坑,查看如下代码: QEventLoop loop; QtConcurrent::run([&]() {doSomething();loop.quit(); }); loop.exec();上述写法存在两个问题: Q…...
基于MongoDB的空间数据存储与查询
一、概念说明 1.1 空间地理数据 MongoDB 中使用 GeoJSON对象 或 坐标对 描述空间地理数据。MongoDB使用 WGS84 参考系进行地理空间数据查询。 1、MongoDB支持空间数据的存储,数据类型需要限制为GeoJSON; 2、MongoDB可以为GeoJSON类型数据建立索引,提升空…...
jquery中pdf的上传、下载及excel导出
jquery中pdf的上传、下载及excel导出 1.PDF上传 pdfUpload2. pdf下载和excel导出用的一种方法,并且需要引入utils.js2.1PDF下载 pdfDownload2.2导出Excel excelExport 1.PDF上传 pdfUpload //PDF上传 pdfUpload window.pdfUploadfunction (obj){layer.open({type:…...

【MyBatis】:PageHelper分页插件与特殊字符处理
目录 一、PageHelper介绍 二、PageHelper使用 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 配置 Mapper.xml 4. 编写测试 三、特殊字符处理 1. 使用转义字符 2. 使用CDATA 区段 一、PageHelper介绍 PageHelper 是 Mybatis 的一个插件,这里就不扯了&a…...

C语言练习1(巩固提升)
C语言练习1 选择题 前言 “人生在勤,勤则不匮。”幸福不会从天降,美好生活靠劳动创造。全面建成小康社会的奋斗目标,为广大劳动群众指明了光明的未来;全面建成小康社会的历史任务,为广大劳动群众赋予了光荣的使命&…...

eCharts热力图Y轴左上角少一块
问题: 如图 在图例的左上角 Y轴会少一块 官方demo https://echarts.apache.org/examples/zh/editor.html?cheatmap-cartesian 事实上 把官方demo的左上角坐标 [ 6, 0, 1 ] 修改为 [ 6, 0, 0 ] 后 依旧会出现该问题 查遍文档 并无解释 也没有任何配置项可解决…...

RabbitMQ介绍
RabbitMQ的概念 RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ 是 一个快递…...

玩转软件|钉钉个人版内测启动:AI探索未来的工作方式
目录 前言 正文 AI为核心,个人效率为王! 指令中心,解锁AI技巧! 灵感Store,探索更多可能! 未来的AI,即将问世! 个人内测体验 前言 重磅消息:钉钉个人版在8月16日正…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...