Skip to content

享元模式解决"大量重复对象占内存"的问题——把不可变的、可共享的部分抽出来复用,而不是每个都新建。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 常量池

相关:《单例模式》(单例是"全局一个",享元是"按内容共享一个")。