运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
享元模式中存在以下两种状态:
享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
享元模式的主要有以下角色:
俄罗斯方块中的方块不需要多次创建,使用享元模式可以省下极大内存空间
创建抽象享元角色
public abstract class AbstractBox {
//相当于内部状态为形状
public abstract String getShape();
//外部状态为颜色
public void getColor(String color){
System.out.println("形状为:"+getShape()+",颜色为:"+color);
}
}
创建具体享元角色
public class IBox extends AbstractBox{
@Override
public String getShape() {
return "I";
}
}
public class LBox extends AbstractBox{
@Override
public String getShape() {
return "L";
}
}
public class OBox extends AbstractBox{
@Override
public String getShape() {
return "O";
}
}
创建享元工厂
public class BoxFactory {
private static BoxFactory boxFactory = new BoxFactory();
//工厂使用单例模式
public static BoxFactory getInstance(){
return boxFactory;
}
private Map map;
public BoxFactory() {
map = new HashMap<>();
map.put("I",new IBox());
map.put("L",new LBox());
map.put("O",new OBox());
}
public AbstractBox getBox(String key){
return map.get(key);
}
}
测试
public class Client {
public static void main(String[] args) {
BoxFactory factory = BoxFactory.getInstance();
AbstractBox IBox = factory.getBox("I");
IBox.getColor("绿色");
AbstractBox LBox = factory.getBox("L");
LBox.getColor("白色");
AbstractBox LBox2 = factory.getBox("L");
LBox2.getColor("黑色");
System.out.println(LBox==LBox2);
}
}
形状为:I,颜色为:绿色
形状为:L,颜色为:白色
形状为:L,颜色为:黑色
true
根据运行结果可以看出来外部状态(颜色)的改变不会影响共享。
为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂
Integer类中的ValueOf()方法就使用到了享元模式
public class Demo {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2);
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);
}
}
ture
false
由结果可以看到这里产生了两种不同的结果,对于int类型的引用变量,装箱为Integer类型的对象,使用的是ValueOf()方法,查看ValueOf源码
这里可以看到,对于传过来的参数有一个判断操作,点进去IntegerCache查看对应的low与high属性是什么
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
这个静态类初始化low为-128以及定义一个high属性以及一个Integer类型的cache数组。
读取官方注释,我们可以猜测出,high属性的值可以在初始化JVM时进行更改。在静态代码块做的事情就是判断high的值是否有设置,如果没有更改就不进行if判断对h的值进行修改,如果修改了high属性值,则要进行判断是否将设置的值对h进行替换作为high属性。
对high进行完赋值后,初始化cache数组,数组长度为最大值与最小值的范围长度。然后通过for循环对范围内的所有值创建成Integer对象存放在数组中
在客户端使用时,先进行判断数组中是否存在了这个数的对象,如果存在直接返回即可,如果不存在则要进行创建Integer对象返回。