深入理解享元模式:用Java实现高效对象共享
享元模式(Flyweight)的核心思想是对象复用,通过共享技术减少内存占用,就像"共享单车"一样让多个调用者共享同一组细粒度对象。
什么是享元模式?
享元模式是一种结构型设计模式,它通过共享技术有效地支持大量细粒度对象的复用。其核心是将对象状态分为:
- 内部状态(Intrinsic):不变的共享部分(如字符编码)
- 外部状态(Extrinsic):变化的非共享部分(如字符位置)
应用场景
- 需要创建海量相似对象(如游戏粒子系统)
- 对象的大部分状态可以外部化
- 内存占用是系统瓶颈
代码示例:实现字体共享系统
假设我们需要渲染文档中的字符,相同字体的字符应该共享字体对象
// 1. 享元接口
interface Font {void render(char c, int x, int y);
}// 2. 具体享元类(内部状态:字体名称、大小)
class ConcreteFont implements Font {private final String fontName;private final int fontSize;public ConcreteFont(String fontName, int fontSize) {this.fontName = fontName;this.fontSize = fontSize;}@Overridepublic void render(char c, int x, int y) {System.out.printf("渲染字符 '%c' 在位置(%d,%d) 使用字体[%s-%dpt]\n", c, x, y, fontName, fontSize);}
}// 3. 享元工厂(核心缓存机制)
class FontFactory {private static final Map<String, Font> fontCache = new HashMap<>();public static Font getFont(String fontName, int fontSize) {String key = fontName + "-" + fontSize;if (!fontCache.containsKey(key)) {System.out.println("创建新字体: " + key);fontCache.put(key, new ConcreteFont(fontName, fontSize));}return fontCache.get(key);}
}// 4. 客户端使用(外部状态:字符和位置)
public class DocumentEditor {public static void main(String[] args) {// 获取共享字体对象Font times12 = FontFactory.getFont("Times New Roman", 12);Font arial14 = FontFactory.getFont("Arial", 14);Font times12_2 = FontFactory.getFont("Times New Roman", 12); // 复用已有对象// 渲染文本(外部状态每次传递)times12.render('H', 10, 20);arial14.render('e', 15, 20);times12_2.render('l', 20, 20); // 复用相同的字体对象}
}
执行结果
创建新字体: Times New Roman-12
创建新字体: Arial-14
渲染字符 'H' 在位置(10,20) 使用字体[Times New Roman-12pt]
渲染字符 'e' 在位置(15,20) 使用字体[Arial-14pt]
渲染字符 'l' 在位置(20,20) 使用字体[Times New Roman-12pt]
享元模式UML图解
享元模式vs对象池
特性 | 享元模式 | 对象池 |
---|---|---|
复用目标 | 不可变对象 | 可重用对象 |
状态管理 | 分离内部/外部状态 | 对象完全独立 |
使用场景 | 海量相似小对象 | 创建成本高的对象 |
典型示例 | 字符/棋子渲染 | 数据库连接池 |
实际应用场景
-
游戏开发:共享树木/建筑纹理
Texture treeTexture = TextureFactory.getTexture("oak"); treeTexture.render(x, y, scale);
-
文档处理:共享字符格式
Font font = FontFactory.getFont("Arial", 12); document.addChar('A', font, position);
-
棋类游戏:共享棋子对象
ChessPiece blackPawn = PieceFactory.getPiece("pawn", BLACK); board.placePiece(blackPawn, x, y);
最佳实践与注意事项
-
线程安全:享元工厂需要同步控制
public static synchronized Font getFont(String key) {// 双重检查锁定if (!cache.containsKey(key)) {cache.put(key, new ConcreteFont(key));}return cache.get(key); }
-
内存监控:防止缓存无限增长
// 使用弱引用防止内存泄漏 Map<String, WeakReference<Font>> cache = new HashMap<>();
-
外部状态管理:确保不依赖内部状态
// 错误示例:将位置存储在享元对象中 class BadFont {private int x, y; // 外部状态不应内部化! }
-
复合享元:组合多个享元对象
class FontStyle {private Font baseFont;private boolean bold;private boolean italic; }
性能对比测试
public class PerformanceTest {public static void main(String[] args) {int count = 100_000;// 测试无享元模式long start1 = System.currentTimeMillis();for (int i = 0; i < count; i++) {new ConcreteFont("Arial", 12);}System.out.println("直接创建耗时: " + (System.currentTimeMillis() - start1) + "ms");// 测试享元模式long start2 = System.currentTimeMillis();for (int i = 0; i < count; i++) {FontFactory.getFont("Arial", 12);}System.out.println("享元模式耗时: " + (System.currentTimeMillis() - start2) + "ms");}
}
测试结果(10万次对象获取):
直接创建耗时: 15ms
享元模式耗时: 3ms
内存占用减少:约99.9%
总结
享元模式本质:用时间换空间,通过增加查找开销减少内存占用
适用条件:
- 系统中存在大量相似对象
- 细粒度对象具备较接近的外部状态
- 需要分离内部/外部状态
设计启示:
- 对象复用比创建新对象更高效
- 不变状态与可变状态分离是优化关键
- 工厂模式是管理共享对象的有效手段
在Java标准库中,
Integer.valueOf()
就是享元模式的经典实现:对于-128到127的整数,会从缓存池中返回共享对象。
通过合理使用享元模式,可以显著降低系统内存消耗,尤其在大规模对象场景下效果惊人。但要注意避免过度设计,只有当对象数量确实导致性能问题时才推荐使用。
相关文章:
深入理解享元模式:用Java实现高效对象共享
享元模式(Flyweight)的核心思想是对象复用,通过共享技术减少内存占用,就像"共享单车"一样让多个调用者共享同一组细粒度对象。 什么是享元模式? 享元模式是一种结构型设计模式,它通过共享技术有…...

OptiStruct实例:消声器前盖ERP分析(2)RADSND基础理论
13.2 Radiated Sound Output Analysis( RADSND ) RADSND 方法通过瑞利积分来求解结构对外的辐射噪声。其基本思路是分为两个阶段,如图 13-12 所示。 图13-12 结构辐射噪声计算示意图 第一阶段采用有限元方法,通过频响分析(模态叠加法、直接法)工况计算结…...

barker-OFDM模糊函数原理及仿真
文章目录 前言一、巴克码序列二、barker-OFDM 信号1、OFDM 信号表达式2、模糊函数表达式 三、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、barker-OFDM 模糊函数②、barker-OFDM 距离分辨率③、barker-OFDM 速度分辨率④、barker-OFDM 等高线图 四、资源自取 前言 本文进行 …...
Linux.docker.k8s基础概念
1.Linux基本命令 cat 查看文件内容。 cd 进入目标目录。 ll 查询当前路劲下文件的详细信息。 ls 查询当前路劲下的文件。 touch 建立一个文件。 mkdir 建立一个文件夹。 rm 删除文件或者目录。 mv 移动目录和重新命名文件。 unzip 解压。 top 查看当前线程的信息。 find …...
GIT命令行的一些常规操作
放弃修改 git checkout . 修改commit信息 git commit --amend 撤销上次本地commit 1、通过git log查看上次提交的哈希值 2、git reset --soft 哈希值 分支 1.创建本地分支 git branch 分支名 2.切换本地分支 git checkout mybranch; 3.创建一个新分支并…...
近期知识库开发过程中遇到的一些问题
我们正在使用Rust开发一个知识库系统,遇到了一些问题,在此记录备忘。 错误:Unable to make method calls because underlying connection is closed 场景:在docker中调用headless_chrome时出错 原因:为减小镜像大小&am…...

3.RV1126-OPENCV 图像叠加
一.功能介绍 图像叠加:就是在一张图片上放上自己想要的图片,如LOGO,时间等。有点像之前提到的OSD原理一样。例如:下图一张图片,在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…...

使用 HTML + JavaScript 实现一个日历任务管理系统
在现代快节奏的生活中,有效的时间管理变得越来越重要。本项目是一个基于 HTML 和 JavaScript 开发的日历任务管理系统,旨在为用户提供一个直观、便捷的时间管理工具。系统不仅能够清晰地展示当月日期,还支持事件的添加、编辑和删除操作&#…...

车载诊断架构SOVD --- 车辆发现与建连
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...

Notepad++找回自动暂存的文件
场景: 当你没有保存就退出Notepad,下次进来Notepad会自动把你上次编辑的内容显示出来,以便你继续编辑。除非你手动关掉当前页面,这样Notepad就会删除掉自动保存的内容。 问题: Notepad会将自动保存的文件地址,打开Note…...

DL00924-基于深度学习YOLOv11的工程车辆目标检测含数据集
文末有代码完整出处 🚗 基于深度学习YOLOv11的工程车辆目标检测——引领智能识别新潮流! 🚀 随着人工智能技术的飞速发展, 目标检测 已经在各个领域取得了显著突破,尤其是在 工程车辆识别 这一关键技术上。今天&#…...

Axure RP11安装、激活、汉化
一:注册码 Axure RP11.0.0.4122在2025-5-29日亲测有效: 49bb9513c40444b9bcc3ce49a7a022f9...
【PhysUnits】15.6 引入P1后的左移运算(shl.rs)
一、源码 代码实现了Rust的类型级二进制数的左移运算(<<),使用类型系统在编译期进行计算。 use super::basic::{Z0, P1, N1, B0, B1, NonZero, NonOne, Unsigned}; use super::sub1::Sub1; use core::ops::Shl;// 左移运算(<<)…...

自编码器Auto-encoder(李宏毅)
目录 编码器的概念: 为什么需要编码器? 编码器什么原理? 去噪自编码器: 自编码器的应用: 特征解耦 离散隐表征 编码器的概念: 重构:输入一张图片,通过编码器转化成向量,要求再…...

数据结构之堆(topk问题、堆排序)
一、堆的初步认识 堆虽然是用数组存储数据的数据结构,但是它的底层却是另一种表现形式。 堆分为大堆和小堆,大堆是所有父亲大于孩子,小堆是所有孩子大于父亲。 通过分析我们能得出父子关系的计算公式,parent(child-1)/2ÿ…...

SpringBoot使用ffmpeg实现视频压缩
ffmpeg简介 FFmpeg 是一个开源的跨平台多媒体处理工具集,用于录制、转换、编辑和流式传输音频和视频。它功能强大,支持几乎所有常见的音视频格式,是多媒体处理领域的核心工具之一。 官方文档:https://ffmpeg.org/documentation.h…...
【Elasticsearch】exists` 查询用于判断文档中是否存在某个指定字段。它检查字段是否存在于文档中,并且字段的值不为 `null`
在 Elasticsearch 中,exists 查询用于判断文档中是否存在某个指定字段。它检查字段是否存在于文档中,并且字段的值不为 null。如果字段存在且有值(即使是空字符串或空数组),则 exists 查询会匹配该文档;如果…...

2025-05-31 Python深度学习9——网络模型的加载与保存
文章目录 1 使用现有网络2 修改网络结构2.1 添加新层2.2 替换现有层 3 保存网络模型3.1 完整保存3.2 参数保存(推荐) 4 加载网络模型4.1 加载完整模型文件4.2 加载参数文件 5 Checkpoint5.1 保存 Checkpoint5.2 加载 Checkpoint 本文环境: Py…...

长安链起链调用合约时docker ps没有容器的原因
在调用这个命令的时候,发现并没有出现官方预期的合约容器,这是因为我们在起链的时候没有选择用docker的虚拟环境,实际上这不影响后续的调用,如果想要达到官方的效果那么你只需要在起链的时候输入yes即可,如图三所示...

Appium+python自动化(七)- 认识Appium- 上
简介 经过前边的各项准备工作,终于才把appium搞定。 一、appium自我介绍 appium是一款开源的自动化测试工具,可以支持iOS和安卓平台上的原生的,基于移动浏览器的,混合的应用(APP)。 1、 使用appium进…...
数据中心双活架构解决方案
数据中心双活架构解决方案 数据中心双活架构(Active-Active Data Center)旨在实现业务高可用、负载均衡和灾难自动切换。以下是完整的解决方案,涵盖架构设计、关键技术、实施步骤及最佳实践。 1. 双活架构设计 1.1 基本架构模型 同城双活(Metro Active-Active) 两个数据…...
YOLOv5 详解:从原理到实战的全方位解析
在计算机视觉领域,目标检测作为核心任务之一,始终吸引着众多研究者和开发者的目光。YOLO(You Only Look Once)系列算法凭借其高效、准确的特点,在目标检测领域占据重要地位。而 YOLOv5 作为 YOLO 系列算法的重要成员&a…...

模块联邦:更快的微前端方式!
什么是模块联邦 在前端项目中,不同团队之间的业务模块可能有耦合,比如A团队的页面里有一个富文本模块(组件),而B团队 的页面恰好也需要使用这个富文本模块。 传统模式下,B团队只能去抄A团队的代码&#x…...

前端基础学习html+css+js
HTML 区块 div标签,块级标签 span包装小部分文本,行内元素 表单 CSS css选择器 css属性 特性blockinlineinline-block是否换行✅ 换行❌ 不换行❌ 不换行可设置宽高✅ 支持❌ 不支持✅ 支持常见元素div容器 p段落 h标题span文本容器 a超链接img图片…...

手机打电话时将对方DTMF数字转为RFC2833发给局域网SIP坐席
手机打电话时将对方DTMF数字转为RFC2833发给局域网SIP坐席 --局域网SIP坐席呼叫 上一篇:手机打电话时由对方DTMF响应切换多级IVR语音菜单(完结) 下一篇:安卓App识别手机系统弹授权框包含某段文字-并自动点击确定按钮 一、前言 …...
TCP三次握手/四次握手-TCP/IP四层模型-SSL/TLS-HTTP-HTTPS
重要概念 seq ( Squence Number ) 序列号,用于数据排序、去重,防止数据包乱序 ack ( Acknowledgement Number ) 确认好,表示期望接受的下一个字节序号,用于确认数据包被对方接受 TCP三次握手是建立可靠连接的过程,确…...

SAP Business One:无锡哲讯科技助力中小企业数字化转型的智慧之选
数字化转型,中小企业的必经之路 在当今竞争激烈的商业环境中,数字化转型已不再是大型企业的专利,越来越多的中小企业开始寻求高效、灵活的管理系统来优化业务流程、提升运营效率。作为全球领先的企业管理软件,SAP Business One…...
【Ubuntu远程桌面】
Ubuntu-远程桌面 ubuntu环境rustdesk-1.4.0-aarch64.deb安装rustdesk注意事项:报错:可能会在远程连接时候显示‘No displays’解决方法1. 安装 CUDA(如果需要)2. 解决 XDG 桌面门户问题3. 检查 RustDesk 客户端日志 总结 kill --t…...
⚡ Linux 系统安装与配置 Vim 编辑器(包括 Vim 插件管理器)
⚡ Linux 系统安装与配置 Vim 编辑器(包括 Vim 插件管理器) 📌 1. Vim 简介 Vim(Vi IMproved)是一款高度可定制的文本编辑器,基于早期的 vi 编辑器扩展而来。 它支持语法高亮、插件扩展、多种编程语言&am…...

小型语言模型:为何“小”才是“大”?
当说到人工智能(AI)的时候,大家通常会想到那些拥有数十亿参数的超大型语言模型,它们能做出一些令人惊叹的事情。 厉害不厉害?绝对厉害! 但对于大多数企业和开发者来说,实用吗?可能…...