My App

享元模式

Flyweight — 共享细粒度对象,大幅减少内存使用

一句话理解

当系统中有大量相似对象时,把它们共同的部分提取出来共享,避免重复创建。

解决什么问题

假设一个文本编辑器要渲染 10 万个字符,如果每个字符都创建一个包含字体、大小、颜色等信息的对象,内存会爆。享元模式把"字体+大小+颜色"这些不变的部分抽出来共享。

核心概念:

  • 内部状态(Intrinsic):不变的、可共享的(如字体、颜色)
  • 外部状态(Extrinsic):变化的、不可共享的(如字符位置)

怎么写

// 享元对象:棋子样式(可共享的内部状态)
public class ChessPiece {
    private final String color;  // 黑/白
    private final String shape;  // 棋子造型

    public ChessPiece(String color, String shape) {
        this.color = color;
        this.shape = shape;
    }

    // 外部状态(位置)由外部传入
    public void place(int x, int y) {
        System.out.println(color + shape + " 放在 (" + x + "," + y + ")");
    }
}

// 享元工厂:缓存并复用已创建的对象
public class ChessPieceFactory {
    private static final Map<String, ChessPiece> cache = new HashMap<>();

    public static ChessPiece get(String color) {
        return cache.computeIfAbsent(color,
            c -> new ChessPiece(c, "●"));
    }
}
// 使用:无论创建多少个同色棋子,内存中只有一个对象
ChessPiece b1 = ChessPieceFactory.get("黑");
ChessPiece b2 = ChessPieceFactory.get("黑");
System.out.println(b1 == b2);  // true,同一个对象

b1.place(0, 0);  // 黑● 放在 (0,0)
b2.place(1, 1);  // 黑● 放在 (1,1) —— 同一个享元,不同的外部状态

Java 中的享元

// String 常量池就是享元模式
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);  // true,共享同一个对象

// Integer 缓存池(-128 ~ 127)
Integer a = 127;
Integer b = 127;
System.out.println(a == b);  // true,从缓存取

Integer c = 128;
Integer d = 128;
System.out.println(c == d);  // false,超出缓存范围

使用场景

  • String 常量池
  • Integer/Long 等包装类的缓存
  • 线程池、连接池 —— 复用已有对象而非每次新建
  • 游戏中大量相同类型的对象(子弹、粒子效果)

On this page