深入解析享元模式:用Java实现高性能对象复用

引言:一场内存危机的启示

想象一下这个场景:你正在开发一个大型文档编辑器,需要渲染成千上万个字符。如果每个字符都创建一个独立的对象,包含字体、大小、颜色等完整属性,你的内存很快就会被耗尽。这正是享元模式要解决的核心问题------在大量细粒度对象共享时,如何有效减少内存占用

享元模式(Flyweight Pattern)是23种设计模式中的结构型模式,它的核心思想是:运用共享技术来有效支持大量细粒度的对象。今天,我将带你深入探索享元模式的Java实现,从基础概念到高级应用。

一、享元模式的核心思想

1.1 两个关键概念

内部状态(Intrinsic State)

  • 对象共享的部分
  • 不会随环境改变
  • 存储在享元对象内部
  • 示例:字符的Unicode编码、棋子的颜色

外部状态(Extrinsic State)

  • 对象特有的部分
  • 随环境变化
  • 由客户端维护
  • 示例:字符的位置、棋子的坐标

1.2 模式结构

复制代码
┌─────────────────────────────────────┐
│           客户端 (Client)             │
│    ┌─────────────────────────┐    │
│    │   享元工厂 (Flyweight    │    │
│    │         Factory)         │    │
│    └───────────┬─────────────┘    │
│                │                    │
│    ┌───────────┼─────────────┐    │
│    ▼           ▼             ▼    │
│ 具体享元     具体享元       享元接口  │
│ (Concrete   (Concrete     (Flyweight)│
│ Flyweight)  Flyweight)              │
└─────────────────────────────────────┘

二、基础实现:文字编辑器案例

让我们从一个简单的文字编辑器开始,看看享元模式如何优化内存使用。

2.1 问题场景

我们需要渲染一个包含10,000个字符的文档。如果每个字符都包含完整属性:

java 复制代码
// 传统方式 - 内存杀手
class Character {
    private char value;
    private String fontFamily;
    private int fontSize;
    private String color;
    private int positionX;
    private int positionY;
    // ... 其他属性
    
    // 10,000个这样的对象会消耗大量内存!
}

2.2 享元模式解决方案

java 复制代码
// 1. 享元接口 - 定义共享操作
public interface CharacterFlyweight {
    void display(CharacterContext context);
}

// 2. 具体享元 - 存储内部状态
public class ConcreteCharacter implements CharacterFlyweight {
    private final char character;
    private final String fontFamily;
    private final int fontSize;
    private final String color;
    
    // 内部状态在构造时确定,之后不可变
    public ConcreteCharacter(char character, String fontFamily, 
                           int fontSize, String color) {
        this.character = character;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
        this.color = color;
    }
    
    @Override
    public void display(CharacterContext context) {
        // 使用内部状态(共享)和外部状态(特有)进行渲染
        System.out.printf("渲染字符 '%c',字体: %s,大小: %d,颜色: %s,位置: (%d, %d)%n",
            character, fontFamily, fontSize, color,
            context.getX(), context.getY());
    }
    
    // 内部状态的getter
    public char getCharacter() { return character; }
    public String getFontFamily() { return fontFamily; }
    public int getFontSize() { return fontSize; }
    public String getColor() { return color; }
}

// 3. 外部状态封装
public class CharacterContext {
    private final int x;
    private final int y;
    
    public CharacterContext(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public int getX() { return x; }
    public int getY() { return y; }
}

// 4. 享元工厂 - 核心的共享机制
public class CharacterFactory {
    // 使用Map缓存已创建的享元对象
    private static final Map<String, ConcreteCharacter> characterPool = 
        new ConcurrentHashMap<>();
    
    // 创建字符的唯一入口
    public static ConcreteCharacter getCharacter(char c, String fontFamily,
                                               int fontSize, String color) {
        // 构建唯一键 - 内部状态的组合
        String key = buildKey(c, fontFamily, fontSize, color);
        
        // 如果已存在,直接返回
        ConcreteCharacter character = characterPool.get(key);
        if (character != null) {
            return character;
        }
        
        // 不存在则创建并缓存
        character = new ConcreteCharacter(c, fontFamily, fontSize, color);
        characterPool.put(key, character);
        
        System.out.println("创建新字符享元: " + key + ",缓存大小: " + 
                          characterPool.size());
        
        return character;
    }
    
    private static String buildKey(char c, String fontFamily, 
                                 int fontSize, String color) {
        return c + "|" + fontFamily + "|" + fontSize + "|" + color;
    }
    
    // 获取缓存统计信息
    public static int getCacheSize() {
        return characterPool.size();
    }
    
    public static void clearCache() {
        characterPool.clear();
    }
}

// 5. 客户端使用
public class TextEditor {
    private final List<Pair<ConcreteCharacter, CharacterContext>> document = 
        new ArrayList<>();
    
    public void addCharacter(char c, String fontFamily, int fontSize,
                           String color, int x, int y) {
        // 获取享元对象(可能来自缓存)
        ConcreteCharacter character = CharacterFactory.getCharacter(
            c, fontFamily, fontSize, color);
        
        // 创建外部状态
        CharacterContext context = new CharacterContext(x, y);
        
        // 存储引用关系
        document.add(new Pair<>(character, context));
    }
    
    public void render() {
        System.out.println("开始渲染文档,共" + document.size() + "个字符");
        System.out.println("享元对象数量: " + CharacterFactory.getCacheSize());
        
        for (Pair<ConcreteCharacter, CharacterContext> entry : document) {
            ConcreteCharacter character = entry.getKey();
            CharacterContext context = entry.getValue();
            character.display(context);
        }
    }
    
    // 内存使用对比
    public void memoryComparison() {
        // 传统方式:每个字符都创建完整对象
        int traditionalMemory = document.size() * estimateCharacterSize();
        
        // 享元模式:共享内部状态
        int flyweightMemory = CharacterFactory.getCacheSize() * 
                            estimateCharacterSize() +
                            document.size() * estimateContextSize();
        
        System.out.printf("\n内存使用对比:\n");
        System.out.printf("传统方式: %d bytes\n", traditionalMemory);
        System.out.printf("享元模式: %d bytes\n", flyweightMemory);
        System.out.printf("节省内存: %.1f%%\n", 
            (1 - (double)flyweightMemory / traditionalMemory) * 100);
    }
    
    private int estimateCharacterSize() {
        // 估算一个Character对象的大小(简化)
        return 64; // bytes
    }
    
    private int estimateContextSize() {
        // 估算Context对象的大小
        return 16; // bytes
    }
}

// 测试类
public class FlyweightDemo {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        
        // 模拟添加10,000个字符
        Random random = new Random();
        String fonts = "Arial,宋体,微软雅黑,Times New Roman";
        String[] fontArray = fonts.split(",");
        String colors = "black,red,blue,green";
        String[] colorArray = colors.split(",");
        
        for (int i = 0; i < 10000; i++) {
            char c = (char) ('A' + random.nextInt(26));
            String font = fontArray[random.nextInt(fontArray.length)];
            int size = 12 + random.nextInt(6); // 12-17px
            String color = colorArray[random.nextInt(colorArray.length)];
            int x = random.nextInt(1000);
            int y = random.nextInt(1000);
            
            editor.addCharacter(c, font, size, color, x, y);
        }
        
        // 渲染文档
        editor.render();
        
        // 显示内存对比
        editor.memoryComparison();
    }
}

运行结果示例

复制代码
创建新字符享元: A|Arial|12|black,缓存大小: 1
创建新字符享元: B|宋体|14|red,缓存大小: 2
...(更多的创建日志)
开始渲染文档,共10000个字符
享元对象数量: 48
渲染字符 'A',字体: Arial,大小: 12,颜色: black,位置: (123, 456)
渲染字符 'B',字体: 宋体,大小: 14,颜色: red,位置: (789, 321)
...

内存使用对比:
传统方式: 640000 bytes
享元模式: 4864 bytes
节省内存: 99.2%

三、进阶应用:围棋棋子享元实现

让我们看一个更复杂的例子------围棋游戏。围棋有361个交叉点,但只有黑白两种棋子。

3.1 完整实现

java 复制代码
// 1. 享元接口
public interface ChessPiece {
    void place(Coordinate coordinate);
    Color getColor();
}

// 2. 具体享元
public class ConcreteChessPiece implements ChessPiece {
    private final Color color; // 内部状态:颜色
    private final String shape; // 内部状态:形状
    
    public ConcreteChessPiece(Color color) {
        this.color = color;
        this.shape = "圆形"; // 固定形状
    }
    
    @Override
    public void place(Coordinate coordinate) {
        System.out.printf("%s棋子放置在位置(%d, %d)%n", 
            color == Color.BLACK ? "黑" : "白",
            coordinate.getX(), coordinate.getY());
    }
    
    @Override
    public Color getColor() {
        return color;
    }
}

// 3. 享元工厂(带线程安全)
public class ChessPieceFactory {
    private static final Map<Color, ChessPiece> piecePool = 
        new ConcurrentHashMap<>();
    
    public static ChessPiece getPiece(Color color) {
        // 双重检查锁定(虽然ConcurrentHashMap不需要,这里展示模式)
        ChessPiece piece = piecePool.get(color);
        if (piece == null) {
            synchronized (ChessPieceFactory.class) {
                piece = piecePool.get(color);
                if (piece == null) {
                    piece = new ConcreteChessPiece(color);
                    piecePool.put(color, piece);
                    System.out.println("创建" + 
                        (color == Color.BLACK ? "黑" : "白") + "棋子享元");
                }
            }
        }
        return piece;
    }
    
    public static int getPieceCount() {
        return piecePool.size();
    }
}

// 4. 棋盘管理
public class ChessBoard {
    private final ChessPiece[][] board;
    private static final int BOARD_SIZE = 19;
    
    public ChessBoard() {
        board = new ChessPiece[BOARD_SIZE][BOARD_SIZE];
    }
    
    public void placePiece(Color color, int x, int y) {
        if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) {
            throw new IllegalArgumentException("位置超出棋盘范围");
        }
        
        if (board[x][y] != null) {
            throw new IllegalStateException("该位置已有棋子");
        }
        
        // 获取享元棋子
        ChessPiece piece = ChessPieceFactory.getPiece(color);
        board[x][y] = piece;
        
        // 放置棋子
        piece.place(new Coordinate(x, y));
    }
    
    public void display() {
        System.out.println("\n当前棋盘状态:");
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] == null) {
                    System.out.print("+");
                } else if (board[i][j].getColor() == Color.BLACK) {
                    System.out.print("●");
                } else {
                    System.out.print("○");
                }
            }
            System.out.println();
        }
        System.out.println("使用的享元对象数量: " + 
                          ChessPieceFactory.getPieceCount());
    }
}

// 5. 测试游戏
public class ChessGame {
    public static void main(String[] args) {
        ChessBoard board = new ChessBoard();
        Random random = new Random();
        
        // 模拟下棋
        for (int i = 0; i < 50; i++) {
            Color color = i % 2 == 0 ? Color.BLACK : Color.WHITE;
            int x = random.nextInt(19);
            int y = random.nextInt(19);
            
            try {
                board.placePiece(color, x, y);
            } catch (IllegalStateException e) {
                // 位置已有棋子,重试
                i--;
            }
        }
        
        board.display();
    }
}

四、享元模式在JDK和框架中的应用

4.1 JDK中的享元模式

Integer缓存
java 复制代码
public class IntegerCacheExample {
    public static void main(String[] args) {
        // Integer的享元模式实现
        Integer a = Integer.valueOf(127);
        Integer b = Integer.valueOf(127);
        System.out.println(a == b); // true,同一个对象
        
        Integer c = Integer.valueOf(128);
        Integer d = Integer.valueOf(128);
        System.out.println(c == d); // false,超出缓存范围
        
        // 查看缓存范围
        System.out.println("Integer缓存范围: " + 
            IntegerCache.low + " 到 " + IntegerCache.high);
    }
    
    // Integer源码中的缓存实现
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        
        static {
            int h = 127;
            // 可以通过JVM参数调整上限
            String integerCacheHighPropValue = 
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch(NumberFormatException nfe) {
                }
            }
            high = h;
            
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
    }
}
String常量池
java 复制代码
public class StringPoolExample {
    public static void main(String[] args) {
        // String常量池是享元模式的典型应用
        String s1 = "Hello";
        String s2 = "Hello";
        System.out.println(s1 == s2); // true,同一个对象
        
        String s3 = new String("Hello");
        String s4 = new String("Hello");
        System.out.println(s3 == s4); // false,不同对象
        
        // intern()方法可以放入常量池
        String s5 = s3.intern();
        System.out.println(s1 == s5); // true
    }
}

4.2 Spring框架中的享元应用

java 复制代码
// Spring中的线程池配置共享
@Configuration
public class ThreadPoolConfig {
    
    @Bean("sharedThreadPool")
    public ExecutorService sharedThreadPool() {
        // 这个线程池被多个服务共享使用
        return Executors.newFixedThreadPool(10);
    }
}

@Service
public class OrderService {
    @Autowired
    @Qualifier("sharedThreadPool")
    private ExecutorService threadPool; // 共享的享元对象
}

@Service  
public class UserService {
    @Autowired
    @Qualifier("sharedThreadPool")
    private ExecutorService threadPool; // 同一个享元对象
}

五、性能优化与最佳实践

5.1 内存优化策略

java 复制代码
/**
 * 享元对象池的优化实现
 * 1. 软引用缓存
 * 2. LRU淘汰策略
 * 3. 并发控制优化
 */
public class OptimizedFlyweightFactory<K, V> {
    private final Map<K, SoftReference<V>> cache;
    private final LinkedHashMap<K, Long> accessLog;
    private final int maxSize;
    private final ReentrantReadWriteLock lock;
    
    public OptimizedFlyweightFactory(int maxSize) {
        this.maxSize = maxSize;
        this.cache = new ConcurrentHashMap<>();
        this.accessLog = new LinkedHashMap<>(16, 0.75f, true);
        this.lock = new ReentrantReadWriteLock();
    }
    
    public V get(K key, Supplier<V> creator) {
        // 读锁(允许多个线程同时读取)
        lock.readLock().lock();
        try {
            SoftReference<V> ref = cache.get(key);
            V value = ref != null ? ref.get() : null;
            
            if (value != null) {
                // 记录访问时间(用于LRU)
                recordAccess(key);
                return value;
            }
        } finally {
            lock.readLock().unlock();
        }
        
        // 缓存未命中,获取写锁
        lock.writeLock().lock();
        try {
            // 双重检查
            SoftReference<V> ref = cache.get(key);
            V value = ref != null ? ref.get() : null;
            
            if (value != null) {
                recordAccess(key);
                return value;
            }
            
            // 创建新对象
            value = creator.get();
            cache.put(key, new SoftReference<>(value));
            recordAccess(key);
            
            // 清理过期引用
            cleanUp();
            
            return value;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    private void recordAccess(K key) {
        accessLog.put(key, System.currentTimeMillis());
    }
    
    private void cleanUp() {
        if (cache.size() > maxSize) {
            // LRU淘汰
            Iterator<Map.Entry<K, Long>> it = accessLog.entrySet().iterator();
            while (it.hasNext() && cache.size() > maxSize * 0.75) {
                Map.Entry<K, Long> entry = it.next();
                // 淘汰最久未使用的
                if (System.currentTimeMillis() - entry.getValue() > 60000) {
                    cache.remove(entry.getKey());
                    it.remove();
                }
            }
        }
    }
    
    // 内存监控
    public void printMemoryStats() {
        Runtime runtime = Runtime.getRuntime();
        long used = runtime.totalMemory() - runtime.freeMemory();
        System.out.printf("内存使用: %dMB, 缓存大小: %d%n",
            used / 1024 / 1024, cache.size());
    }
}

5.2 线程安全实现

java 复制代码
/**
 * 线程安全的享元工厂
 */
public class ThreadSafeFlyweightFactory {
    
    // 方案1:使用ConcurrentHashMap(推荐)
    private final Map<String, Object> cache1 = new ConcurrentHashMap<>();
    
    public Object getFlyweight1(String key) {
        return cache1.computeIfAbsent(key, k -> createFlyweight(k));
    }
    
    // 方案2:双重检查锁定(适用于非并发集合)
    private final Map<String, Object> cache2 = new HashMap<>();
    
    public Object getFlyweight2(String key) {
        Object flyweight = cache2.get(key);
        if (flyweight == null) {
            synchronized (cache2) {
                flyweight = cache2.get(key);
                if (flyweight == null) {
                    flyweight = createFlyweight(key);
                    cache2.put(key, flyweight);
                }
            }
        }
        return flyweight;
    }
    
    // 方案3:使用Guava Cache
    private final Cache<String, Object> cache3 = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .build();
    
    public Object getFlyweight3(String key) throws ExecutionException {
        return cache3.get(key, () -> createFlyweight(key));
    }
    
    private Object createFlyweight(String key) {
        // 创建享元对象的逻辑
        return new Object();
    }
}

六、享元模式的局限性

6.1 何时不使用享元模式

java 复制代码
// 反模式示例:不应该使用享元的情况
public class WrongFlyweightUse {
    
    // 情况1:对象变化频繁
    class Player {
        private String name;        // 变化频繁
        private int health;         // 变化频繁
        private Position position;  // 变化频繁
        
        // 这些属性经常变化,不适合作为内部状态
    }
    
    // 情况2:对象数量很少
    // 如果只有几个对象,享元模式带来的复杂性不值得
    
    // 情况3:外部状态管理复杂
    class ComplexContext {
        // 如果外部状态过于复杂,管理成本可能超过节省的内存
        
        private Map<String, Object> states;
        private List<Dependency> dependencies;
        private Configuration config;
        // ... 太多外部状态
    }
}

6.2 识别合适的场景

适合使用享元模式的情况:

  • ✅ 系统中存在大量相似对象
  • ✅ 对象的大部分状态可以外部化
  • ✅ 使用享元后能显著减少内存占用
  • ✅ 对象标识不重要(可以共享)

不适合使用享元模式的情况:

  • ❌ 对象需要频繁变化状态
  • ❌ 对象的数量很少
  • ❌ 外部状态过于复杂
  • ❌ 需要维护对象间的独立关系

七、现代应用:连接池与缓存

7.1 数据库连接池的享元实现

java 复制代码
/**
 * 数据库连接池 - 享元模式的现代应用
 */
public class DatabaseConnectionPool {
    private final List<Connection> pool;
    private final List<Connection> usedConnections;
    private final int maxSize;
    
    public DatabaseConnectionPool(String url, String user, 
                                 String password, int maxSize) throws SQLException {
        this.maxSize = maxSize;
        this.pool = new ArrayList<>(maxSize);
        this.usedConnections = new ArrayList<>();
        
        // 预先创建连接
        for (int i = 0; i < maxSize; i++) {
            pool.add(DriverManager.getConnection(url, user, password));
        }
    }
    
    public synchronized Connection getConnection() throws InterruptedException {
        while (pool.isEmpty()) {
            if (usedConnections.size() < maxSize) {
                // 可以创建新连接
                try {
                    pool.add(createNewConnection());
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            } else {
                // 等待连接释放
                wait();
            }
        }
        
        Connection connection = pool.remove(pool.size() - 1);
        usedConnections.add(connection);
        return new PooledConnection(connection);
    }
    
    public synchronized boolean releaseConnection(Connection connection) {
        if (connection instanceof PooledConnection) {
            PooledConnection pooled = (PooledConnection) connection;
            usedConnections.remove(pooled.getRealConnection());
            pool.add(pooled.getRealConnection());
            notifyAll();
            return true;
        }
        return false;
    }
    
    private Connection createNewConnection() throws SQLException {
        // 实际项目中从配置读取
        return DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/test",
            "root",
            "password"
        );
    }
    
    // 包装类,用于正确释放连接
    private class PooledConnection implements Connection {
        private final Connection realConnection;
        
        PooledConnection(Connection realConnection) {
            this.realConnection = realConnection;
        }
        
        Connection getRealConnection() {
            return realConnection;
        }
        
        @Override
        public void close() throws SQLException {
            releaseConnection(this);
        }
        
        // 委托所有Connection方法给realConnection
        @Override
        public Statement createStatement() throws SQLException {
            return realConnection.createStatement();
        }
        
        // ... 其他方法委托
    }
}

7.2 与Spring框架集成

java 复制代码
@Configuration
public class FlyweightConfiguration {
    
    @Bean
    @Scope("singleton")
    public FlyweightFactory flyweightFactory() {
        return new FlyweightFactory();
    }
    
    @Bean
    @Scope("prototype") // 每次获取新实例
    public ClientService clientService() {
        return new ClientService();
    }
}

@Service
public class ClientService {
    @Autowired
    private FlyweightFactory factory;
    
    public void process() {
        // 使用享元对象
        Flyweight flyweight = factory.getFlyweight("key");
        flyweight.operation(new ExternalState());
    }
}

// AOP监控享元使用情况
@Aspect
@Component
public class FlyweightMonitor {
    
    @Around("execution(* FlyweightFactory.getFlyweight(..))")
    public Object monitorFlyweightUsage(ProceedingJoinPoint joinPoint) throws Throwable {
        String key = (String) joinPoint.getArgs()[0];
        long start = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - start;
            
            // 记录指标
            Metrics.recordFlyweightAccess(key, duration, 
                result instanceof CachedFlyweight);
            
            return result;
        } catch (Exception e) {
            Metrics.recordFlyweightError(key);
            throw e;
        }
    }
}

八、性能测试与对比

java 复制代码
public class FlyweightBenchmark {
    
    public static void main(String[] args) {
        int objectCount = 100000;
        
        // 测试传统方式
        long traditionalTime = testTraditional(objectCount);
        long traditionalMemory = getUsedMemory();
        
        // 测试享元模式
        long flyweightTime = testFlyweight(objectCount);
        long flyweightMemory = getUsedMemory();
        
        System.out.println("\n===== 性能测试结果 =====");
        System.out.printf("对象数量: %,d%n", objectCount);
        System.out.printf("传统方式 - 时间: %,d ms, 内存: %,d bytes%n", 
            traditionalTime, traditionalMemory);
        System.out.printf("享元模式 - 时间: %,d ms, 内存: %,d bytes%n", 
            flyweightTime, flyweightMemory);
        System.out.printf("时间提升: %.1f%%%n", 
            (1 - (double)flyweightTime / traditionalTime) * 100);
        System.out.printf("内存节省: %.1f%%%n", 
            (1 - (double)flyweightMemory / traditionalMemory) * 100);
    }
    
    private static long testTraditional(int count) {
        List<TraditionalObject> list = new ArrayList<>(count);
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < count; i++) {
            TraditionalObject obj = new TraditionalObject(
                "key" + (i % 100), // 只有100种不同的内部状态
                i, i * 2, i * 3    // 外部状态各不相同
            );
            list.add(obj);
        }
        
        return System.currentTimeMillis() - start;
    }
    
    private static long testFlyweight(int count) {
        List<Pair<Flyweight, ExternalState>> list = new ArrayList<>(count);
        FlyweightFactory factory = new FlyweightFactory();
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < count; i++) {
            Flyweight flyweight = factory.getFlyweight("key" + (i % 100));
            ExternalState state = new ExternalState(i, i * 2, i * 3);
            list.add(new Pair<>(flyweight, state));
        }
        
        return System.currentTimeMillis() - start;
    }
    
    private static long getUsedMemory() {
        Runtime runtime = Runtime.getRuntime();
        runtime.gc(); // 建议GC,但不确定会立即执行
        return runtime.totalMemory() - runtime.freeMemory();
    }
}

// 测试类定义
class TraditionalObject {
    private final String intrinsicState;
    private final int extrinsic1;
    private final int extrinsic2;
    private final int extrinsic3;
    
    public TraditionalObject(String intrinsicState, int e1, int e2, int e3) {
        this.intrinsicState = intrinsicState;
        this.extrinsic1 = e1;
        this.extrinsic2 = e2;
        this.extrinsic3 = e3;
    }
}

class Flyweight {
    private final String intrinsicState;
    
    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }
}

总结

享元模式通过共享对象的内在状态,显著减少了大量相似对象的内存占用。它特别适用于以下场景:

  1. 大量相似对象存在:如文档编辑器中的字符、棋类游戏中的棋子
  2. 对象的大部分状态可以外部化:如位置、方向等可以独立存储
  3. 内存是主要约束:在内存受限的环境中特别有效

关键收获

  • 分清内部状态(共享)和外部状态(特有)
  • 使用工厂模式管理享元对象的创建和缓存
  • 注意线程安全和性能优化
  • 结合现代框架(如Spring)和工具(如Guava Cache)使用
相关推荐
qq_401700412 小时前
Qt的.pro文件
开发语言·qt
qq_401700412 小时前
Qt 事件处理机制
java·数据库·qt
FAFU_kyp2 小时前
Rust 的 引用与借用
开发语言·算法·rust
喵星人工作室2 小时前
C++传说:神明之剑0.4.5装备机制彻底完成
开发语言·c++·游戏
秦jh_2 小时前
【Qt】系统相关(下)
开发语言·qt
东木月2 小时前
使用python获取Windows产品标签
开发语言·windows·python
pumpkin845142 小时前
Go 基础语法全景
开发语言·后端·golang
hqwest2 小时前
码上通QT实战18--监控页面10-获取设备数据
开发语言·qt·湿度·modbus功能码·寄存器地址·从站数据·0103
星火开发设计2 小时前
C++ multiset 全面解析与实战指南
开发语言·数据结构·c++·学习·set·知识