享元模式的核心是共享内在状态、分离外在状态;通过工厂缓存不可变的轻量对象(如字符及样式),客户端传入坐标、颜色等上下文信息,避免重复创建,适用于海量微型对象场景。

享元模式的核心思想:共享不变状态,分离可变状态
享元(Flyweight)模式本质是“以时间换空间”——把对象中**不随上下文变化的内在状态(Intrinsic State)** 提取出来共享,而将**依赖具体场景的外在状态(Extrinsic State)** 由客户端传入。对字符这类微型对象(如 char、String 字面量、Font 样式等),其本身不可变、种类有限,天然适合享元化。
Java 中字符级享元的典型实现:CharFlyweightFactory
Java 已内置部分享元优化(如 String 常量池、Character 缓存 -128~127),但若需自定义控制(如支持全 Unicode 字符缓存、带样式/颜色等轻量属性),可手动构建享元工厂:
- 定义 Flyweight 接口或抽象类,只暴露不修改内部数据的方法(如
render(int x, int y)) - 实现具体享元类(如
CharFlyweight),字段仅含char value、int fontSize等共享属性,**不含 position、color 等每次渲染才确定的值** - 用 ConcurrentHashMap 或 EnumMap(若字符集固定)做享元缓存池,键为组合特征(如
"A-12-BOLD"或new CharKey('A', 12, true)) - 工厂类提供
getFlyweight(char c, int size, boolean bold)方法,查缓存、缺则创建并缓存
外在状态如何传递?避免重新封装对象
享元对象本身不保存坐标、颜色、透明度等每次使用才不同的信息。客户端应直接在调用时传入:
- 渲染时:调用
flyweight.draw(graphics, x, y, Color.RED, alpha) - 文本布局时:将
CharFlyweight与LineMetrics分离,位置计算由外部排版引擎完成 - 禁止在享元内设
setX()、setColor()等方法——这会破坏共享性,导致线程不安全或状态污染
实战注意点:何时用?何时不用?
享元不是银弹。以下情况值得引入:
- 系统存在数万+ 相同结构的微型对象(如富文本编辑器里每个字符实例、游戏地图中的瓦片图标)
- 对象创建/销毁开销显著(如涉及 native 资源、反射初始化)
- 内存分析确认对象占堆比例高(用 JProfiler / VisualVM 查
char[]、String实例数)
以下情况无需强行享元:
- 字符总数少于几百个,JVM 自身缓存已覆盖(如只用 ASCII 字母)
- 每个对象的“外在状态”极其复杂,传参成本超过对象复用收益
- 业务逻辑要求对象具备独立生命周期或可变行为(享元必须是不可变或只读的)
'a' 和 'A' 必须区分,字体大小用 int 而非 float 避免精度问题。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











