Java设计模式之享元模式详解
一、享元模式核心思想
核心目标:通过共享技术高效支持大量细粒度对象,减少内存消耗。将对象分为内部状态(共享)和外部状态(不可共享),如同活字印刷术重复使用字模。
二、享元模式类图(Mermaid)
三、代码实现示例
1. 围棋棋子共享场景
// 抽象享元
interface ChessPiece {
void draw(int x, int y); // 外部状态:坐标
}
// 具体享元(内部状态:颜色)
class BlackChess implements ChessPiece {
private final String color = "黑色"; // 内部状态
public void draw(int x, int y) {
System.out.printf("%s棋子落子(%d,%d)\n", color, x, y);
}
}
// 享元工厂
class ChessFactory {
private static final Map<String, ChessPiece> pool = new HashMap<>();
public static ChessPiece getChess(String color) {
return pool.computeIfAbsent(color, c -> {
if ("黑色".equals(c)) return new BlackChess();
if ("白色".equals(c)) return new WhiteChess();
throw new IllegalArgumentException();
});
}
}
// 客户端调用
ChessPiece black1 = ChessFactory.getChess("黑色");
ChessPiece black2 = ChessFactory.getChess("黑色"); // 复用对象
black1.draw(10, 20);
black2.draw(15, 25);
四、模式优缺点分析
✅ 优势
- 内存优化:减少重复对象的创建(如10万个字符只需26个字母对象)
- 性能提升:降低垃圾回收压力
- 集中管理:通过工厂统一控制共享对象
❌ 缺点
- 增加系统复杂度:需要分离内部/外部状态
- 线程安全风险:共享对象需设计为不可变
- 调试困难:对象复用可能导致逻辑追踪困难
五、典型应用场景
- 文本编辑器:字符对象池(ASCII共128个字符)
- 游戏开发:粒子系统(火花、雨滴等特效复用)
- 数据库连接池:复用已建立的连接
- GUI系统:图标/按钮样式共享
- 棋牌游戏:麻将/扑克牌面图案复用
六、Mermaid序列图(对象获取流程)
七、享元模式 vs 其他模式
对比模式 | 核心区别 |
---|---|
单例模式 | 控制实例数量而非对象共享 |
对象池模式 | 管理临时对象复用,享元对象永久存在 |
代理模式 | 控制访问而非共享对象 |
八、内部状态 vs 外部状态
状态类型 | 特点 | 示例 |
---|---|---|
内部状态 | 对象固有属性,可共享 | 棋子的颜色、字符的Unicode |
外部状态 | 对象上下文信息,不可共享 | 棋子的位置、字符的坐标 |
九、实际框架应用案例
1. Java String常量池
String s1 = "享元"; // 加入常量池
String s2 = "享元"; // 复用常量池对象
String s3 = new String("享元"); // 新建对象
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
2. Java Integer缓存池
Integer i1 = 127; // 使用缓存
Integer i2 = 127;
Integer i3 = 128; // 新建对象
Integer i4 = 128;
System.out.println(i1 == i2); // true
System.out.println(i3 == i4); // false
十、常见问题解答
Q1:如何保证享元对象的线程安全?
- 设计为不可变对象:所有属性用final修饰
- 使用线程安全集合:如ConcurrentHashMap管理对象池
Q2:如何处理需要不同的享元对象?
通过组合方式扩展:
Q3:享元模式与缓存有何区别?
- 缓存:临时存储数据,可能被清除
- 享元:永久存储对象,生命周期与应用一致
掌握享元模式,能有效优化内存密集型应用的性能。记住:不是所有重复对象都适合共享,需权衡内存节省与代码复杂度!
如果文章对你有帮助,请帮忙点个关注吧,谢谢啦