1. 介绍
- 享元模式是对象池的一种实现,用来尽可能地减少内存使用量,适用于可能存在大量重复对象的场景,用来缓存可共享的对象,避免创建过多对象,从而提升性能、避免内存溢出等。
- 享元对象中的部分状态是可共享的,称为内部状态,它不会受到环境变化的影响;部分状态是不可共享的,称为外部状态,会受到环境变化的影响。
- 在享元模式中会建立一个Map对象容器,键为可共享的内部状态,值为对象本身,用容器缓存对象达到重复利用相似对象的目的
2. 定义
- 享元模式是池技术的重要实现方式,它可有效地支持大量的细粒度对象的重复利用,从而减少应用程序对象的创建,降低程序内存的占用,提高程序的性能。
3. UML类图

- Flyweight:享元对象抽象基类或接口
- ConcreteFlyweight:具体享元对象
- FlyweightFactory:享元工厂,负责管理享元对象池和创建享元对象
4. 使用场景
- 系统中存在大量的相似对象。
- 需要缓冲池的场景。
- 细粒度的对象都具有较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。
5. 简单实现
- 需求:每年的春运,高铁站卖高铁票,服务器不可能每卖出一张票就创建一个高铁票对象,而是使用享元模式将高铁票对象进行处理,这里只是简单模拟,将高铁票车次当作不可共享的外部状态,高铁票价格以及座位当作可共享的内部状态。
- 定义享元对象接口
public interface ITicket {
void showTicketInfo(String seat);
}
public class Ticket implements ITicket {
private final String number;
private int price;
private String seat;
public Ticket(String number) {
this.number = number;
}
@Override
public void showTicketInfo(String seat) {
if ("一等座".equals(seat)) {
price = 400 + (int) (Math.random() * 400);
} else {
price = new Random().nextInt(400);
}
System.out.println(number + "车次-" + seat + "-价格:" + price);
}
}
public class TicketFactory {
private static HashMap<String, ITicket> map = new HashMap<>();
public static ITicket getTicket(String number) {
if (map.containsKey(number)) {
System.out.println("使用享元模式缓存的对象" + number);
return map.get(number);
} else {
System.out.println("创建对象并缓存" + number);
ITicket ticket = new Ticket(number);
map.put(number, ticket);
return ticket;
}
}
}
public class Client {
public static void main(String[] args) {
ITicket ticket1 = TicketFactory.getTicket("G111");
ticket1.showTicketInfo("一等座");
ITicket ticket2 = TicketFactory.getTicket("G111");
ticket2.showTicketInfo("二等座");
ITicket ticket3 = TicketFactory.getTicket("G222");
ticket3.showTicketInfo("无座");
}
}
运行结果:
创建对象并缓存G111
G111车次-一等座-价格:509
使用享元模式缓存的对象G111
G111车次-二等座-价格:382
创建对象并缓存G222
G222车次-无座-价格:66
6. 源码中的使用场景
- Android的Handler机制中的Message对象(享元模式的思想,使用了链表结构存储对象,而不是传统的map存储)
7. 优缺点
- 优点:
- 大幅度减少内存中对象的数量,对象重复利用,避免频繁GC,提高程序性能
- 缺点:
- 享元模式使得系统更加复杂。为了对象可共享,需将一些状态外部化,并得对享元对象进行管理,使逻辑变复杂
- 将享元状态外部化,而读取外部状态会导致运行时间稍稍变长