介绍
享元模式是对象池的一种实现。享元模式可以用开减少内存的使用量,她适合用于存在大量的重复对象的场景,来缓存可共享的对象,达到对象共享,避免创建过多的对象的效果,这样一来就可以提升性能,避免内存溢出等。
享元对象中的部分状态是可以共享的,可以共享的状态为内部状态,内部状态不会随着环境的变化而变化;不可以共享的状态称为外部状态,他们会随着环境的变化而变化。在享元模式中会创建一个对象容器,在经典的享元模式中概容器为一个Map,他的键是享元对象的内部状态,他的值是享元对象本身。客户端程序通过这个内部状态从享元工厂中获取享元对象,如果有缓存则使用缓存对象,否则创建一个享元对象并且存入容器中,这样一来就避免了创建过多对象的问题。
使用场景
简单实现
坐火车购票,当数以万计的人不间断的在请求数据的时候,如果每次都重新创建一个查询结果的话,那么必然会造成大量的重复对象的创建,销毁,使得GC任务繁重,内存占用率居高不下。这类问题可以通过享元模式得到很好的解决。我们可以将公用的对象缓存起来,用户使用的时候,优先使用缓存,如果没有缓存则重新创建,这就使得成千上万的对象变成了可选择的有限的数量。
1 2 3 4
| public interface Ticket {
public void showTicketInfo(String bunko); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class TrainTicket implements Ticket {
public String from; public String to; public String bunk;
public int price;
public TrainTicket(String from,String to){ this.from=from; this.to=to; }
@Override public void showTicketInfo(String bunk) { price=new Random().nextInt(300); System.out.println("购买从"+from+"到"+to+""+"的"+bunk+"火车票,"+"价格:"+price+"元。"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class TicketFactory {
static Map<String,Ticket> map = new ConcurrentHashMap<>();
public static Ticket getTicket(String from,String to){
String key = from+"-"+to; if(map.containsKey(key)){ System.out.println("使用缓存"+key); return map.get(key); }else{ System.out.println("创建对象"+key); Ticket ticket = new TrainTicket(from,to); map.put(key,ticket); return ticket; }
}
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void main(String[] args){
Ticket ticket1 = TicketFactory.getTicket("武汉","杭州"); ticket1.showTicketInfo("上铺");
Ticket ticket2 = TicketFactory.getTicket("武汉","杭州"); ticket1.showTicketInfo("中铺");
Ticket ticket3 = TicketFactory.getTicket("武汉","杭州"); ticket1.showTicketInfo("下铺");
Ticket ticket4 = TicketFactory.getTicket("武汉","杭州"); ticket1.showTicketInfo("坐票");
}
|
在JDK中String也是类似消息池,Java中的String存放在常量池中,也就是说一个String被定义之后,他就被缓存到了常量池中,当其他地方要使用同样的字符串的时候,则直接使用的是缓存,而不会重复创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public static void main(String[] args){
String str1 = new String("abc"); String str2 = "abc"; String str3 = new String("abc"); String str4 = "ab"+"c";
//equals只判定字符值 System.out.println(str1.equals(str2)); System.out.println(str1.equals(str3)); System.out.println(str3.equals(str2));
//==判断两个对象的地址是否相等 System.out.println(str1==str2); System.out.println(str1==str3); System.out.println(str3==str2); System.out.println(str4==str2);
}
|
打印结果:
1 2 3 4 5 6 7
| true true true false false false true
|
由于str1,str2,str3,str4内容相等,所以equals返回的是true;
str1跟str3都是通过new创建的,而str2则是通过字面赋值的,因此这三个判定都是false,因为他们并不是同一个对象,而str2跟str4是通过字面值赋值的,也就是直接通过双引号设置的字符串值,因此,最后一个通过”==”判定的值是true,也就是说str2跟str4是同一个字符串对象。因为str4使用了缓存常量池中的str2对象。这就是享元模式在我们开发中的一个重要的案例。
上一篇:迭代器模式
下一篇:外观模式