深入解析享元模式:用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)使用
相关推荐
晓13136 小时前
第七章 【C语言篇:文件】 文件全面解析
linux·c语言·开发语言
愚者游世6 小时前
Delegating Constructor(委托构造函数)各版本异同
开发语言·c++·程序人生·面试·改行学it
一 乐6 小时前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
KIKIiiiiiiii6 小时前
微信个人号API二次开发中的解决经验
java·人工智能·python·微信
梵刹古音6 小时前
【C语言】 指针基础与定义
c语言·开发语言·算法
80530单词突击赢6 小时前
SpringBoot整合SpringMVC全解析
java·spring boot·后端
Ekehlaft6 小时前
这款国产 AI,让 Python 小白也能玩转编程
开发语言·人工智能·python·ai·aipy
rit84324996 小时前
MATLAB中Teager能量算子提取与解调信号的实现
开发语言·matlab
开源技术6 小时前
Python GeoPandas基础知识:地图、投影和空间连接
开发语言·ide·python
vx1_Biye_Design6 小时前
基于Spring Boot+Vue的学生管理系统设计与实现-计算机毕业设计源码46223
java·vue.js·spring boot·spring·eclipse·tomcat·maven