享元模式解决"大量重复对象占内存"的问题——把不可变的、可共享的部分抽出来复用,而不是每个都新建。Integer 缓存、String 常量池就是它的落地。这篇讲清它怎么省内存。
一、解决什么问题:大量重复对象
有些场景会创建海量"内容重复"的对象。比如一个文本编辑器,每个字符都是一个对象,但"a"字符有成千上万个——如果每个都 new,内存爆炸。
享元的思路:把"字符对象"抽成共享的,相同的字符共用一个对象,只把"位置、颜色"这些不同的部分放在外部。
二、核心:内部状态 vs 外部状态
享元把对象状态分成两部分:
| 状态 | 是什么 | 能否共享 |
|---|---|---|
| 内部状态 | 对象固有、不变的部分 | 能共享 |
| 外部状态 | 随场景变化的部分 | 不能共享(由客户端传入) |
java
// 享元:字符对象,内部状态(字符本身)可共享
class Character {
private final char symbol; // 内部状态:不可变,可共享
Character(char symbol) { this.symbol = symbol; }
// 外部状态(位置、颜色)由客户端传入,不存进对象
void display(int x, int y, String color) {
System.out.println(symbol + " 在 (" + x + "," + y + ") 颜色 " + color);
}
}
// 享元工厂:缓存共享对象
class CharacterFactory {
private static final Map<Character, Character> cache = new HashMap<>();
public static Character get(char symbol) {
return cache.computeIfAbsent(symbol, Character::new); // 已存在就复用
}
}
// 使用:一千个 'a',工厂只 new 一次,其余都复用同一个对象
Character c1 = CharacterFactory.get('a');
Character c2 = CharacterFactory.get('a');
// c1 == c2,是同一个对象关键:内部状态(字符本身)放进共享对象,外部状态(位置、颜色)不存进去,调用时传参。这样相同的字符只占一份内存。
三、Java 里的享元
Java 本身就用了享元:
java
Integer a = Integer.valueOf(127);
Integer b = Integer.valueOf(127);
System.out.println(a == b); // true,-128~127 缓存了,复用同一个对象
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true,字符串常量池复用Integer 缓存(-128~127)和 String 常量池,本质就是享元——相同内容复用同一个对象,省内存。
四、小结
- 享元共享"不可变的内部状态",节省大量重复对象的内存
- 内部状态可共享,外部状态(位置/颜色)由客户端传参
- Java 落地:Integer 缓存、String 常量池
相关:《单例模式》(单例是"全局一个",享元是"按内容共享一个")。
