享元模式

介绍

享元模式是对象池的一种实现。享元模式可以用开减少内存的使用量,她适合用于存在大量的重复对象的场景,来缓存可共享的对象,达到对象共享,避免创建过多的对象的效果,这样一来就可以提升性能,避免内存溢出等。

享元对象中的部分状态是可以共享的,可以共享的状态为内部状态,内部状态不会随着环境的变化而变化;不可以共享的状态称为外部状态,他们会随着环境的变化而变化。在享元模式中会创建一个对象容器,在经典的享元模式中概容器为一个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对象。这就是享元模式在我们开发中的一个重要的案例。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器