享元模式(如果缓存中有从缓存中拿,缓存没有就创建)
场景:网站外包
我们想要一个网站以不同的形式展现出来。
传统做法是每需要一个网站就new一个这样的对象出来,这样的缺点是系统内部会出现大量相似的对象,使得系统效率降低,要是网站出现了bug,所有创建的对象都要重新创建,维护起来会很麻烦。假如有多个用户想要以新闻的形式发布,我们可以让这些用于共享一个以新闻发布的网站,这样就不用重复地创建相同的对象,网站要是出现了bug,修改起来也会很容易,这也就是下面所说的享元模式
享元模式
也叫蝇量模式,常用于系统底层优化性能,解决重复对象的内存浪费问题,当系统中由大量相似对象,需要内存时,不需要总是创建新的对象,而是一次使用的时候,将创建的对象放入缓冲池中,其他人再使用的时候直接从缓冲池中那就行了,这样就可以避免创建大量的重复对象。享元模式在Java中用的非常多比如各种连接池(提前创建好连接对象,要用的时候从池中直接拿,用完再放回去),String类(使用的字符串都会从常量池中拿,如果没有则创建一个字符串放入常量池,再把它的引用拿过来赋值)
内部状态:对象的类别(黑白两种)
外部状态:对象赖以生存的,不可共享标记(在这个坐标上需要棋子时,直接从缓冲区中拿,而不需要再次创建对象)(谁来使用这个共享的对象)
实现
网站的抽象类:
@Data
public abstract class WebSite {
private String type;
abstract void use(User user);
}
网站具体的实现类:
public class ConcreteWebsite extends WebSite{
ConcreteWebsite(String type){
setType(type);
}
@Override
void use(User user) {
System.out.println("当前网站的类型是:"+getType()+"\n使用的用户是:"+user.getName());
}
}
用户:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String name;
}
网站工厂,里面封装了网站的使用池
其中内部模式是type,也就是网站的类型,而外部模式是用户(线程)。每个网站有不同的类型,而每个类型最多只会有一个示例,所有用户共享这些示例,这样就可以避免创建过多的重复对象,提供系统性能
public class WebFactory {
private static final WebFactory webFactory=new WebFactory();
private static final HashMap<String,WebSite> webSitePool=new HashMap<>(3);
private WebFactory(){}
public static WebFactory getWebFactory(){
return webFactory;
}
public WebSite getWebSite(String type){
if(webSitePool.containsKey(type)){
return webSitePool.get(type);
}else{
WebSite webSite=new ConcreteWebsite(type);
webSitePool.put(type,webSite);
return webSite;
}
}
public int getWebSiteCount(){
return webSitePool.size();
}
}
得到结果:
微信形式的网站调用了两次,但是只创建了一个对象
是不是和Spring容器很像?Spring的整个框架就是基于享元模式来实现了,所有的组件都放在单例池中,如果我们设置的是单例对象,获取对象时会先从单例池中找,如果没有找到就创建一个组件。(当然,创建对象的流程远不止这些)
享元模式源码示例:Integer
我们来查看valueof的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Integer在类加载的时候,会开一个数组存放值为[-128,127]的Integer对象,如果我们需要这个范围的Integer对象,它就会直接将缓存中的对象返回,如果不在这个返回内就会创建一个新的Integer对象。(因为[-128,127]使用频率较高),这也是x==z的原因,因为x,z获取的都是cache中的对象,所以是是同一个对象,而x1,x2因为不在[-128,127]这个范围内,所以会创建两个不同的对象