Java大白話講解享元模式(Flyweight)

編程語言 Java Java虛擬機 技術 編程界的小學生 編程界的小學生 2017-09-16

一、概念

一個系統中若存在大量相同或相似的對象(比如26個英文字母),則只共享一份就可以了(並非單例模式),沒有必要為每份相同的東西還都單獨實例化出一個對象,浪費性能。(有點緩存的意思)

二、例如

比如26個英文字母,一個文本中有10MB的26個英文字母,那你解析出來的時候需要為每一個字母都創建一個對象的話(10M那麼大,對象數不可直視。。。),內存肯定扛不住。所以可以用享元模式,只創建26個對象進行共享就行了。

三、那麼什麼時候使用此模式呢?

  • 一個應用程序使用了大量(少量的話完全沒必要)的對象。

  • 完全由於使用大量的對象,造成很大的存儲開銷。(對象存到堆內存,對象太多,堆內存扛不住的時候)

  • 對象的大多數狀態都可變為外部狀態

  • 如果刪除對象的外部狀態,那麼可以用相對較少的共享對象取代很多組成對象

  • 應用程序不依賴對象標識。

四、優缺點

優點

大幅度地降低內存中對象的數量,減緩堆內存壓力。

缺點

1)享元模式使得系統更加複雜。為了使對象可以共享,需要將一些狀態外部化,這使得程序的邏輯複雜化。

2)享元模式將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。

總的來說,享元模式一般是解決系統性能問題的,所以經常用於底層開發,在項目開發中並不常用。

五、圖解

Java大白話講解享元模式(Flyweight)

圖解

1)抽象享元角色(Flyweight):此角色是所有的具體享元類的超類,為這些類規定出需要實現的公共接口或抽象類。那些需要外部狀態(External State)的操作可以通過方法的參數傳入。抽象享元的接口使得享元變得可能,但是並不強制子類實行共享,因此並非所有的享元對象都是可以共享的。

2)具體享元(ConcreteFlyweight)角色:實現抽象享元角色所規定的接口。如果有內部狀態的話,必須負責為內部狀態提供存儲空間。享元對象的內部狀態必須與對象所處的周圍環境無關,從而使得享元對象可以在系統內共享。有時候具體享元角色又叫做單純具體享元角色,因為複合享元角色是由單純具體享元角色通過複合而成的。

3) 複合享元(UnsharableFlyweight)角色:複合享元角色所代表的對象是不可以共享的,但是一個複合享元對象可以分解成為多個本身是單純享元對象的組合。複合享元角色又稱作不可共享的享元對象。這個角色一般很少使用。

4)享元工廠(FlyweightFactoiy)角色:本角色負責創建和管理享元角色。本角色必須保證享元對象可以被系統適當地共享。當一個客戶端對象請求一個享元對象的時候,享元工廠角色需要檢查系統中是否已經有一個符合要求的享元對象,如果已經有了,享元工廠角色就應當提供這個已有的享元對象;如果系統中沒有一個適當的享元對象的話,享元工廠角色就應當創建一個新的合適的享元對象。

5)客戶端(Client)角色:本角色還需要自行存儲所有享元對象的外部狀態。

六、內部狀態與外部狀態

在享元對象內部並且不會隨著環境改變而改變的共享部分,可以稱之為享元對象的內部狀態,反之隨著環境改變而改變的,不可共享的狀態稱之為外部狀態。

七、案例

Java大白話講解享元模式(Flyweight)

實體

Java大白話講解享元模式(Flyweight)

抽象享元角色

Java大白話講解享元模式(Flyweight)

具體享元角色

Java大白話講解享元模式(Flyweight)

享元工廠角色

Java大白話講解享元模式(Flyweight)

測試類

結果表明,本來需要四個對象,運用享元模式後,只用了兩個對象。減少了一半。建議在大量對象的時候使用。

八、JDK中的享元模式

Integer、Lang、Byte、String等都用到了享元模式

Integer、Lang、Byte中的valueOf()方法用到了享元模式,String常量池就是享元模式。

實例來驗證我的說法

Java大白話講解享元模式(Flyweight)

Java大白話講解享元模式(Flyweight)

解釋:

(1)String的常量池即用到了享元模式,所以為true。他首先檢查常量池中是否有str這個值,若有則直接指向,不會重新創建String對象。(共享),但是每次都new String的話是不行的,因為jvm遇到new指令後就會創建一個對象出來,無法沿用常量池。

(2)(3)(4)(5)(6)(7)是因為Integer(Long、Byte等)內部維護著一個Cache,用於共享。可以查看Integer的源碼,如下:(Long、Byte也是一樣的)

Java大白話講解享元模式(Flyweight)

Integer享元模式源碼

可發現,若值在-128~127之間,是可以直接從Cache中獲取的,好比String常量池,直接從池中獲取,無需new。若不再這範圍內,才會去new Integer(i)返回新對象。否則共享。所以享元模式來減少對象數目,達到優化還是很牛的。

若有興趣,歡迎來加入群,【Java初學者學習交流群】:458430385,此群有Java開發人員、UI設計人員和前端工程師。有問必答,共同探討學習,一起進步!

歡迎關注我的微信公眾號【Java碼農社區】,會定時推送各種乾貨(純乾貨的東西,並非Java基礎語法這些。)

Java大白話講解享元模式(Flyweight)

Java碼農社區

相關推薦

推薦中...