本文源碼: GitHub·點這裡 || GitEE·點這裡
一、使用場景
應用代碼
public class C01_InScene {
public static void main(String[] args) {
String c0 = "cicada" ;
String c1 = "cicada" ;
System.out.println(c0 == c1); // true
}
}
String類型就是使用享元模式。String對象是final類型,對象一旦建立就不可改變。在JAVA中字元串常量都是存在常量池中的,JAVA會確定一個字元串常量在常量池中隻有一個拷貝,其中c0和c1就是一個字元串常量。結果為:true,說明c0和c1兩個引用都指向了常量池中的同一個字元串常量"cicada"。這樣的設計避免了在建立N多相同對象時所産生的不必要的大量的資源消耗。
二、享元模式
1、基礎簡介
享元模式是對象的結構模式。享元模式以共享的方式高效地支援大量的細粒度對象。常用于系統底層開發,解決系統的性能問題。像資料庫連接配接池,裡面都是建立好的連接配接對象,在這些連接配接對象中有需要的則直接拿來用,避免重新建立。
2、模式圖解

3、核心角色
- 抽象享元角色
給出一個抽象接口,以規定具體享元角色要實作的方法。
- 具體享元角色
實作抽象享元角色所規定出的接口。如果有内部狀态的話,必須為内部狀态提供存儲能力。
- 享元工廠角色
負責建立和管理享元角色。必須保證享元對象可以被系統選擇性共享。當一個用戶端對象調用一個享元對象的時候,享元工廠角色會檢查系統中是否已經有一個符合要求的享元對象。如果已經存在,直接傳回該對象;如果系統中沒有該享元對象,享元工廠角色就應當建立該享元對象。
4、内部外部狀态
- 内部狀态指對象共享出來的資訊,存儲在享元對象内部且不會随環境的改變而改變。
- 外部狀态指對象得以依賴的一個标記,可以随環境改變而改變的、不可共享的狀态。
5、源碼實作
public class C02_FlyWeight {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly = factory.factoryMethod('c');
fly.printState("One");
fly = factory.factoryMethod('d');
fly.printState("Two");
fly = factory.factoryMethod('c');
fly.printState("San");
// 調用了三次工廠方法,隻建立了兩個對象
factory.poolInfo();
}
}
/**
* 抽象享元角色類
*/
interface Flyweight {
void printState (String state);
}
/**
* 具體享元角色類
*/
class ConcreteFlyweight implements Flyweight {
private Character innerState = null;
public ConcreteFlyweight (Character state){
this.innerState = state;
}
@Override
public void printState(String state) {
System.out.println("内部狀态:" + this.innerState);
System.out.println("外部狀态:" + state);
}
}
/**
* 享元工廠角色類
*/
class FlyweightFactory {
// 模拟資料池
private Map<Character,Flyweight> pool = new HashMap<>();
public Flyweight factoryMethod (Character state){
//先從緩存中查找對象
Flyweight fly = pool.get(state);
if(fly == null){
fly = new ConcreteFlyweight(state);
pool.put(state, fly);
}
return fly;
}
public void poolInfo (){
System.out.println("資料池:"+pool);
}
}
三、JDK應用場景
- 測試代碼塊
public class C03_Integer {
public static void main(String[] args) {
Integer c1 = Integer.valueOf(127),c2 = Integer.valueOf(127) ;
Integer c3 = new Integer(127),c4 = new Integer(127) ;
boolean flag1 = c1==c2 ,flag2 = c2==c3,flag3 = c3==c4 ;
// true;false;false
System.out.println(flag1+";"+flag2+";"+flag3);
Integer c5 = Integer.valueOf(222),c6=Integer.valueOf(222) ;
// false
System.out.println(c5==c6);
}
}
- 源碼分析
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
這裡valueOf方法的意思就是,如果傳入的資料在 (-128) 到 (127) 之間,則從緩存中直接傳回,否則建立一個新的Integer對象。
四、模式總結
享元模式能夠解決重複對象的記憶體浪費的問題,當系統中有大量相似對象,需要緩沖池時。不需一直建立新對象,可以從緩沖池裡拿。這樣可以降低系統記憶體,同時提高效率。經典的應用場景就是池技術,String常量池、資料庫連接配接池、緩沖池等等都是享元模式的應用,享元模式是池技術的重要實作方式。享元模式使得系統更加複雜。為了使對象可以共享,需要時刻管理對象的狀态變化,這使得程式的邏輯變得複雜。
五、源代碼位址
GitHub·位址
https://github.com/cicadasmile/model-arithmetic-parent
GitEE·位址
https://gitee.com/cicadasmile/model-arithmetic-parent