1. 從Integer說起
下面的例子中,num1 == num2這個表達(dá)式輸出的是true,num3 == num4這個表達(dá)式輸出的是false,如你所知,Integer將了-128~127之間Integer對象緩存起來了,這些數(shù)值較小的對象,使用的頻率較高,復(fù)用可以提高效率。
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類內(nèi)部有一個IntegerCache靜態(tài)內(nèi)部類,緩存了-128~127之間Integer對象,便于我們復(fù)用。
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 最大值可以配置,默認(rèn)是127
int h = 127;
// 略去讀取配置最大值的代碼
high = h;
// 創(chuàng)建要被緩存起來的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)方法,當(dāng)i處于-128~127之間時,總是返回被緩存的Integer對象,達(dá)到復(fù)用的目的。這個方法,和工廠模式中獲取產(chǎn)品的方法是相似的。
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)是結(jié)構(gòu)性設(shè)計模式,關(guān)注點在實現(xiàn)對象的復(fù)用,即運用共享技術(shù)有效地支持大量細(xì)粒度的對象。中文“享元”是意譯,更貼近本設(shè)計模式的本意,所謂“元”,即是“細(xì)顆粒度的對象”;所謂“享”,即是復(fù)用。java的String、Integer類都使用了享元模式,常用的場景還有線程池、數(shù)據(jù)庫連接池等。
享元模式一共有三種角色。
抽象享元(Flyweight)角色 :給出一個抽象接口,以規(guī)定出所有具體享元角色需要實現(xiàn)的方法。這些方法可以向外界提供享元對象的內(nèi)部數(shù)據(jù)(內(nèi)蘊(yùn)狀態(tài)),同時也可以通過這些方法來設(shè)置外部數(shù)據(jù)(外蘊(yùn)狀態(tài))。
具體享元類(ConcreteFlyweight):具體的享元對象,實現(xiàn)抽象享元接口。在具體享元類中為內(nèi)部狀態(tài)提供了存儲空間。通常我們可以結(jié)合單例模式來設(shè)計具體享元類,為每一個具體享元類提供唯一的享元對象。
享元工廠(FlyWeight Factory): 主要用來創(chuàng)建并管理共享的享元對象,并對外提供訪問共享享元的接口。

/**
* 抽象享元角色
*/
public interface Flyweight {
// 參數(shù)state是外蘊(yùn)狀態(tài)
public void operate(String state);
}
/**
* 具體享元角色
*/
public class ConcreteFlyweight implements Flyweight {
/**內(nèi)部的狀態(tài)*/
private String internalState;
public ConcreteFlyweight(String internalState) {
this.internalState = internalState;
}
/**
* 一個示意性的方法,可以對內(nèi)蘊(yùn)狀態(tài)(數(shù)據(jù))和外蘊(yùn)狀態(tài)(數(shù)據(jù))做操作。
*
* @param externalState 外部狀態(tài)
*/
@Override
public void operate(String externalState) {
System.out.println("內(nèi)蘊(yùn)狀態(tài):" + internalState);
System.out.println("外蘊(yùn)狀態(tài):" + externalState);
}
}
/**
* 享元工廠角色
*/
public class FlyweightFactory {
//定義一個HashMap用于存儲享元對象,實現(xiàn)享元池
private Map<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
//如果對象存在,則直接從享元池獲取。
if (flyweights.containsKey(key)) {
return (Flyweight) flyweights.get(key);
} else { //如果對象不存在,則創(chuàng)建一個新的對象,并添加到享元池中,然后返回。
Flyweight flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
return flyweight;
}
}
}
3. 題外話
跑個題,下表是國際職業(yè)拳擊聯(lián)盟對拳手17個級別的劃分,可見叫“FlyWeight”級別的拳手要求體重小于52.16千克,對于一個成年人來說,這樣的體重實在太輕了,是“細(xì)顆粒度”的。我們猜測,把此模式成為FlyWeight,是因為要復(fù)用的對象是細(xì)顆粒度的緣故。
| 序號 | 級別 | 劃分依據(jù) | 級別英文名 |
|---|---|---|---|
| 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 |
(完)