1. 从Integer说起
下面的例子中,num1 == num2
这个表达式输出的是true,num3 == num4
这个表达式输出的是false,如你所知,Integer将了-128~127之间Integer对象缓存起来了,这些数值较小的对象,使用的频率较高,复用可以提高效率。
Integer num1 = 127;
Integer num2 = 127;
Integer num3 = 128;
Integer num4 = 128;
//true
System.out.println(num1 == num2);
//false
System.out.println(num3 == num4);
Integer类内部有一个IntegerCache
静态内部类,缓存了-128~127之间Integer对象,便于我们复用。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 最大值可以配置,默认是127
int h = 127;
// 略去读取配置最大值的代码
high = h;
// 创建要被缓存起来的Integer对象
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() {}
}
Integer还提供了valueOf(int i)方法,当i处于-128~127之间时,总是返回被缓存的Integer对象,达到复用的目的。这个方法,和工厂模式中获取产品的方法是相似的。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
2. 享元模式简介
享元模式(FlyWeight Pattern)是结构性设计模式,关注点在实现对象的复用,即运用共享技术有效地支持大量细粒度的对象。中文“享元”是意译,更贴近本设计模式的本意,所谓“元”,即是“细颗粒度的对象”;所谓“享”,即是复用。java的String、Integer类都使用了享元模式,常用的场景还有线程池、数据库连接池等。
享元模式一共有三种角色。
-
抽象享元(Flyweight)角色 :给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。这些方法可以向外界提供享元对象的内部数据(内蕴状态),同时也可以通过这些方法来设置外部数据(外蕴状态)。
-
具体享元类(ConcreteFlyweight):具体的享元对象,实现抽象享元接口。在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
-
享元工厂(FlyWeight Factory): 主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。
/**
* 抽象享元角色
*/
public interface Flyweight {
// 参数state是外蕴状态
public void operate(String state);
}
/**
* 具体享元角色
*/
public class ConcreteFlyweight implements Flyweight {
/**内部的状态*/
private String internalState;
public ConcreteFlyweight(String internalState) {
this.internalState = internalState;
}
/**
* 一个示意性的方法,可以对内蕴状态(数据)和外蕴状态(数据)做操作。
*
* @param externalState 外部状态
*/
@Override
public void operate(String externalState) {
System.out.println("内蕴状态:" + internalState);
System.out.println("外蕴状态:" + externalState);
}
}
/**
* 享元工厂角色
*/
public class FlyweightFactory {
//定义一个HashMap用于存储享元对象,实现享元池
private Map<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
//如果对象存在,则直接从享元池获取。
if (flyweights.containsKey(key)) {
return (Flyweight) flyweights.get(key);
} else { //如果对象不存在,则创建一个新的对象,并添加到享元池中,然后返回。
Flyweight flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
return flyweight;
}
}
}
3. 题外话
跑个题,下表是国际职业拳击联盟对拳手17个级别的划分,可见叫“FlyWeight”级别的拳手要求体重小于52.16千克,对于一个成年人来说,这样的体重实在太轻了,是“细颗粒度”的。我们猜测,把此模式成为FlyWeight,是因为要复用的对象是细颗粒度的缘故。
序号 | 级别 | 划分依据 | 级别英文名 |
---|---|---|---|
1 | 草量级(迷你轻量级) | 47.627kg以下(105磅) | Minimumweight |
2 | 次蝇量级(特轻量级) | 不到48.980kg(108磅) | Light Flyweight |
3 | 蝇量级(次最轻量级) | 不到50.800kg(112磅) | Flyweight |
4 | 次雏量级(超次最轻量级) | 不到52.160kg(115磅) | Super Flyweight |
5 | 雏量级(最轻量级) | 不到53.520kg(118磅) | Bantamweight |
6 | 次羽量级 | 不到55.340kg(122磅) | Super Bantamweight |
7 | 羽量级 | 不到57.150kg(126磅) | Featherweight |
8 | 次轻量级 | 不到58.970kg(130磅) | Super Featherweight |
9 | 轻量级 | 不到61.230kg(135磅) | Lightweight |
10 | 超轻量级 | 不到63.504kg(140磅) | Light Welterweight |
11 | 次中量级 | 不到66.680kg(147磅) | Welterweight |
12 | 超次中量级 | 不到69.850kg(154磅) | Light Middleweight |
13 | 中量级 | 不到72.570kg(160磅) | Middleweight |
14 | 超中量级 | 不到76.204kg(168磅) | Super Middleweight |
15 | 轻重量级 | 不到79.380kg(175磅) | Light Heavyweight |
16 | 次重量级 | 不到86.180kg(190磅) | Cruiserweight |
17 | 重量级 | 86.180kg以上(190磅以上) | Heavyweight |
(完)
网友评论