面向?qū)ο笤O(shè)計原則與設(shè)計模式_第1頁
面向?qū)ο笤O(shè)計原則與設(shè)計模式_第2頁
面向?qū)ο笤O(shè)計原則與設(shè)計模式_第3頁
面向?qū)ο笤O(shè)計原則與設(shè)計模式_第4頁
面向?qū)ο笤O(shè)計原則與設(shè)計模式_第5頁
已閱讀5頁,還剩87頁未讀 繼續(xù)免費閱讀

付費下載

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

面向?qū)ο笤O(shè)計原則&設(shè)計模式

?面向?qū)ο笤O(shè)計原則

?開閉原則(OpenClosePrinciple)

對擴展開放,對修改關(guān)閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現(xiàn)一個熱插拔

的效果。

?作用

?1.對軟件測試的影響

軟件遵守開閉原則的話,軟件測試時只需要對擴展的代碼進行測試就可以了,因為原有的

測試代碼仍然能夠正常運行。

?2.可以提高代碼的可復用性

粒度越小,被復用的可能性就越大;在面向?qū)ο蟮某绦蛟O(shè)計中,根據(jù)原子和抽象編程可以

提高代碼的可復用性。

?3.可以提高軟件的可維護性

遵守開閉原則的軟件,其穩(wěn)定性高和延續(xù)性強,從而易于擴展和維護。

?實現(xiàn)方式

通過"抽象約束、封裝變化”來實現(xiàn)開閉原則,即通過接口或者抽象類為軟件實體定義一個相

對穩(wěn)定的抽象層,而將相同的可變因素封裝在相同的具體實現(xiàn)類中。

?里氏代換原則(LiskovSubstitutionPrinciple)

子類可以擴展父類的功能,但不能改變父類原有的功能。一個軟件實體如果使用的是一個父類的話,

那么一定適用于其子類,而且它察覺不出父類對象和子類對象之間的區(qū)別。也就是說,把父類都替

換成它的子類,程序的行為沒有發(fā)生變化。

?定義總結(jié)

?子類可以實現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法。

?子類中可以增加自己特有的方法。

?當子類的方法重載父類的方法時,方法的前置條件(即方法的輸入?yún)?shù))要比父類的方法

更寬松。

如果子類重載父類的方法時,方法的參數(shù)比父類方法更嚴格,此時不能保證子類在什么地

方都可以替換父類。

?當子類的方法實現(xiàn)父類的方法時(重寫/重載或?qū)崿F(xiàn)抽象方法),方法的后置條件(即方法

的的輸出/返回值)要比父類的方法更嚴格或相等。

如果子類返回比父類寬松,也不能保證在什么地方都可以替換父類。例如:父類返回鳥

(會飛),子類返回動物(不是所有動物都會飛).

?作用

?里氏替換原則是實現(xiàn)開閉原則的重要方式之一。

?它克服了繼承中重寫父類造成的可復用性變差的缺點.

?它是動作正確性的保證。即類的擴展不會給已有的系統(tǒng)引入新的錯誤,降低了代碼出錯的

可能性。

?加強程序的健壯性,同時變更時可以做到非常好的兼容性,提高程序的維護性、可擴展性,

降低需求變更時引入的風險。

?依賴倒轉(zhuǎn)原則(DependenceInversionPrinciple)

核心思想:要面向接口編程,不要面向?qū)崿F(xiàn)編程。目的:通過要面向接口的編程來降低類間的耦

合性。在軟件設(shè)計中,細節(jié)具有多變性,而抽象層則相對穩(wěn)定,因此以抽象為基礎(chǔ)搭建起來的架構(gòu)

要比以細節(jié)為基礎(chǔ)搭建起來的架構(gòu)要穩(wěn)定得多。抽象指的是接口或者抽象類,而細節(jié)是指具體的實

現(xiàn)類。

?作用

?降低類間的耦合性。

?提高系統(tǒng)的穩(wěn)定性。

遵循開閉原則,是實現(xiàn)開閉原則的重要途徑之一。

?減少并行開發(fā)引起的風險。

如果依賴實現(xiàn)類,由于實現(xiàn)類是會變更的,風險高。

?提高代碼的可讀性和可維護性。

?實現(xiàn)原則的4點要求

?每個類盡量提供接口或抽象類,或者兩者都具備。

?變量的聲明類型盡量是接口或者是抽象類。

?任何類都不應該從具體類派生。

?使用繼承時盡量遵循里氏替換原則。

?舉例說明:

?顧客購物程序

分析科辨反映了”1?客美?與”商店類.的關(guān)系.藥店類中有sellQ方法,瞬客類通過iS方法由物以下代碼定義了附落類通過曲關(guān)網(wǎng)店

ShaoguanShop的物:

OLclassCustoaer1

02.publicvoidshopping〈ShaoguanShopshopIi

01,川粕

04Syatva.out.printInlihop.a?ll0):

05.)

06.1

但JI,這斡設(shè)計存在缺點,如果該做客也從第夕1家商店(如要理網(wǎng)店WuyuanShop)陣物.就要將幽客的代6修改如下

0Lcl&sxCuitaner(

01publiciruidshopping(VuyuanShopshop){

01/硒

04.Svstea.out.printin(shop.s?110);

01)

0&}

顧客15更換一冢酒店,都要修改一次代碼,這明罪違背了開閉朦?1.存在以上缺點的蛔是:頤客類及計時間具體的商店類御定了,這違雷了

依硒■原則.M決方法且定義11要身網(wǎng)店一和窗關(guān)網(wǎng)店”的共同接□Shop.Sfi客類面向該按口給程,其代瑪修改如下

0LciassCustcner{

OXpublicvoiddnppini(Shopshop){

01M狗

04.Systea.out.printIn(shop.s?110):

05.)

?顧客購物程序類圖

?單一職責原則(SingleResponsibilityPrinciple)

一個類應該有且只有一個變化的原因。所謂職責是指類變化的原因。如果一個類有多于一個的動機

被改變,那么這個類就具有多于一個的職責。核心就是控制類的粒度大小、將對象解耦、提高其內(nèi)

聚性。單一職責原則適用于類、接口、方法。

?優(yōu)點

?降低類的復雜度。

一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單得多。

?提高類的可讀性。

復雜性降低,自然其可讀性會提高。

?提高系統(tǒng)的可維護性。

可讀性提高,那自然更容易維護了.

?變更引起的風險降低。

變更是必然的,如果單一職責原則遵守得好,當修改一個功能時,可以顯著降低對其他功

能的影響。

?高內(nèi)聚,彳氐耦合

?高內(nèi)聚:內(nèi)聚是指類內(nèi)部的屬性和行為,高內(nèi)聚就是指:一個類的屬性和行為與這個類非

常密切,稱為高內(nèi)聚。

?低耦合:耦合是指類與類之間或者模塊與模塊之間的聯(lián)系,低耦合就是指:耦合度低,易

重用、使用靈活。

?接口隔離原則(InterfaceSegregationPrinciple)

客戶端不應該被迫依賴于它不使用的方法。一個類對另一個類的依賴應該建立在最小的接口上。

以上兩個定義的含義是:要為各個類建立它們需要的專用接口,而不要試圖去建立一個很龐大的接

口供所有依賴它的類去調(diào)用。

?與單一職責原則的區(qū)別

都是為了提高類的內(nèi)聚性、降低它們之間的耦合性,體現(xiàn)了封裝的思想,但兩者是不同的。

?單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。

?單一職責原則主要是約束類,它針對的是程序中的實現(xiàn)和細節(jié);接口隔離原則主要約束接

□,主要針對抽象和程序整體框架的構(gòu)建。

?優(yōu)點

?提高系統(tǒng)的靈活性和可維護性。

將臃腫龐大的接口分解為多個粒度小的接口,可以預防外來變更的擴散。

?接口隔離提高了系統(tǒng)的內(nèi)聚性,減少了對外交互,降低了系統(tǒng)的耦合性。

?如果接口的粒度大小定義合理,能夠保證系統(tǒng)的穩(wěn)定性。

如果定義過小,則會造成接口數(shù)量過多,使設(shè)計復雜化;如果定義太大,靈活性降低,無

法提供定制服務(wù),給整體項目帶來無法預料的風險。

?使用多個專門的接口還能夠體現(xiàn)對象的層次。

因為可以通過接口的繼承,實現(xiàn)對總接口的定義

?能減少項目工程中的代碼冗余。

過大的大接口里面通常放置許多不用的方法,當實現(xiàn)這個接口的時候,被迫設(shè)計冗余的代

碼。

?舉例說明

?學生成績管理程序

學生成績管理程序一般包含插入成績、刪除成績、修改成績、計算總分、計算均分、打印

成績信息、查詢成績信息等功能,如果將這些功能全部放到一個接口中顯然不太合理,正

確的做法是將它們分別放在輸入模塊、統(tǒng)計模塊和打印模塊等3個模塊中。

?學生成績管理程序的類圖

ooo

InputModuleCountModulePrintModule

+insert():void+countTotalScore():void+printstulnfb():void

+delete():void+countAverage():void+queryStuInfb():void

+modifyV():void

實現(xiàn)類

StuScoreList

-StuScoreList()

+getlnpulModule():InputModule

+getCountModule():CountModule

+getPrintModule():PrintModule

+insert():void

+delete():void

+modify():void

+countTotalScore():void

+countAverage():void

+printStuInfd():void

+queryStulnfo():void

?迪米特法則(最少知識原則)(DemeterPrinciple)

定義是:只與你的直接朋友交談,不跟“陌生人"說話。其含義是:如果兩個軟件實體無須直接通

信,那么就不應當發(fā)生直接的相互調(diào)用,可以通過第三方轉(zhuǎn)發(fā)該調(diào)用。其目的是降低類之間的耦合

度,提高模塊的相對獨立性。

?優(yōu)點

?降低了類之間的耦合度,提高了模塊的相對獨立性。

?由于親合度降低,從而提高了類的可復用率和系統(tǒng)的擴展性。

?總結(jié)

?從依賴者的角度來說,只依賴應該依賴的對象。

?從被依賴者的角度說,只暴露應該暴露的方法。

?舉例說明

?明星由于全身心投入藝術(shù),所以許多日常事務(wù)由經(jīng)紀人負責處理,如與粉絲的見面會,與

媒體公司的業(yè)務(wù)洽淡等.這里的經(jīng)紀人是明星的朋友,而粉絲和媒體公司是陌生人,所以

適合使用迪米特法則。

?明星與經(jīng)紀人關(guān)系類圖

?合成復用原則(CompositeReusePrinciple)

要求在軟件復用時,要盡量先使用組合或者聚合等關(guān)聯(lián)關(guān)系來實現(xiàn),其次才考慮使用繼承關(guān)系來實

現(xiàn).

?復用分類

?繼承復用

繼承復用雖然有簡單和易實現(xiàn)的優(yōu)點,但它也存在以下缺點。

?繼承復用破壞了類的封裝性。

因為繼承會將父類的實現(xiàn)細節(jié)暴露給子類,父類對子類是透明的,所以這種復用又稱為

“白箱"復用。

?子類與父類的耦合度高。

父類的實現(xiàn)的任何改變都會導致子類的實現(xiàn)發(fā)生變化,這不利于類的擴展與維護。

?它限制了復用的靈活性。

從父類繼承而來的實現(xiàn)是靜態(tài)的,在編譯時已經(jīng)定義,所以在運行時不可能發(fā)生變化。

?合成復用

可以將已有對象納入新對象中,使之成為新對象的一部分,新對象可以調(diào)用已有對象的功

能,它有以下優(yōu)點。

?它維持了類的封裝性。

因為成分對象的內(nèi)部細節(jié)是新對象看不見的,所以這種復用又稱為“黑箱"復用。

?新舊類之間的耦合度低。

這種復用所需的依賴較少,新對象存取成分對象的唯一方法是通過成分對象的接口。

?復用的靈活性高。

這種復用可以在運行時動態(tài)進行,新對象可以動態(tài)地引用與成分對象類型相同的對象。

?舉例說明

?汽車分類管理

汽車按"動力源"劃分可分為汽油汽車、電動汽車等;按"顏色"劃分可分為白色汽車、

黑色汽車和紅色汽車等。如果同時考慮這兩種分類,其組合就很多。

?繼承方式實現(xiàn)

用繼承關(guān)系實現(xiàn)會產(chǎn)生很多子類,而且增加新的“動力源"或者增加新的"顏色"都要修

改源代碼,這違背了開閉原則,顯然不可取。

?復合方式實現(xiàn)

?設(shè)計模式

?模式的分類

?按目的分類

主要用來完成什么工作來劃分。

?創(chuàng)建型模式

主要用于創(chuàng)建對象

?工廠方法模式(FactoryMethod)

?抽象工廠模式(AbstractFactory)

?單例模式(Singleton)

?原型模式(Prototype)

?建造者模式(Builder)

?結(jié)構(gòu)型模式

主要用于處理類或?qū)ο蟮慕M合

?適配器模式(Adapter)

?橋接模式(Bridge)

?組合模式(Composite)

?裝飾模式(Decorator)

?外觀模式(Facade)

?享元模式(Flyweight)

?代理模式(Proxy)

?行為型模式

主要用于描述對類或?qū)ο笤鯓咏换ズ驮鯓臃峙渎氊?/p>

?職責鏈模式(ChainofResponsibility)

?命令模式(Command)

?解釋器模式(Interpreter)

?迭代器模式(Iterator)

?中介者模式(Mediator)

?備忘錄模式(Memento)

?觀察者模式(Observer)

?狀態(tài)模式(State)

?策略模式(Strategy)

?模板方法模式(TemplateMethod)

?訪問者模式(Visitor)

?按范圍分類

主要用于類上還是主要用于對象上來分.

?類模式

處理類和子類之間的關(guān)系,這些關(guān)系通過繼承建立,在編譯時刻就被確定下來,是屬于靜

態(tài)的.

?工廠方法模式(FactoryMethod)

?適配器模式(Adapter)

?模板方法模式(TemplateMethod)

?解釋器模式(Interpreter)

?對象模式

處理對象間的關(guān)系,這些關(guān)系在運行時刻變化,更具動態(tài)性。

?除類模式(4個)之外的19個模式都屬對象模式

?創(chuàng)建型模式

主要關(guān)注點是“怎樣創(chuàng)建對象?",它的主要特點是"將對象的創(chuàng)建與使用分離"。

?單例模式

指一個類只有一個實例,且該類能自行創(chuàng)建這個實例的一種模式。

?結(jié)構(gòu)圖

單例類Singleton

-instance:SingletonO

訪問類Client

-Singleton()

+getlnstance():Singleton#instance:Singleton

publicstaticsynchronizedSingletorgetlnstance(){4

if(instance==null){//用懶漢方式實現(xiàn)

instance=newSingleton();

{else{

System.out.println(“單例對象已經(jīng)創(chuàng)建!”);

}

returninstance;

J

?特點

?單例類只有一個實例對象;

?該單例對象必須由單例類自行創(chuàng)建;

?單例類擁有一個私有構(gòu)造函數(shù),確保用戶無法通過new關(guān)鍵字直接實例化它;

?單例類向外提供一個靜態(tài)的公有函數(shù)用于創(chuàng)建或獲取該靜態(tài)私有實例;

?目的

?保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

?優(yōu)點

?可以保證內(nèi)存里只有一個實例,減少了內(nèi)存的開銷。

?設(shè)置全局訪問點,可以優(yōu)化和共享資源的訪問。

?可以避免對資源的多重占用。

?缺點

?單例模式一般沒有接口,擴展困難。

如果要擴展,則除了修改原來的代碼,沒有第二種途徑,違背開閉原則.

?單例模式的功能代碼通常寫在一個類中,如果功能設(shè)計不合理,則很容易違背單一職責

原則。

?適用場景

?需要頻繁創(chuàng)建的一些類,使用單例可以降低系統(tǒng)的內(nèi)存壓力,減少GC。

?某類只要求生成一個對象的時候,如一個班中的班長。

?某些類創(chuàng)建實例時占用資源較多,或?qū)嵗臅r較長,且經(jīng)常使用。

?某類需要頻繁實例化,而創(chuàng)建的對象又頻繁被銷毀的時候,如多線程的線程池、網(wǎng)絡(luò)連

接池等。

?頻繁訪問數(shù)據(jù)庫或文件的對象。

?當對象需要被共享的場合。

由于單例模式只允許創(chuàng)建一個對象,共享該對象可以節(jié)省內(nèi)存,并加快對象訪問速度。

如Web中的配置對象、數(shù)據(jù)庫的連接池等。

?實現(xiàn)方式

?方式一:懶漢式

該模式的特點是類加載時沒有生成單例,只有當?shù)谝淮握{(diào)用getlnstance方法時才去創(chuàng)

建這個單例。

?實現(xiàn)方式

?普通方式

IpublicclassL紙丫負口呂1d9。(

privatestaticLazySingletoninstance=null;

privateLazySingletonO{}

publicstaticLazySingletone**etln...s..t.a...n.ceO{

if(instance==null){

instance=newLazySingletonO;

)

returninstance;

)

)

多線程下,會出線程安全問題。

?阻塞方式

publicclassLazySingleton{

〃保證instance在所有線程中同步

privatestaticvolatileLazySingletoninstance=null,

privateLazySingletonO0//priva.例化

publicstaticsynchronizedLazySingletongetlnstanceO{

//getInstance方法前加同步

if(instance--null){

instance=newLazySingletonO:

)

returninstance.

)

)

解決了線程安全問題,但是每次調(diào)用getlnstance()時都需要進行線程鎖定判斷,

在多線程高并發(fā)環(huán)境中,將會導致系統(tǒng)性能大大降低。

?volatile

synchronized是阻塞式同步,在線程競爭激烈的情況下會升級為重量級鎖。

而volatile可以說是java虛擬機提供的最輕量級的同步機制。Java內(nèi)存模

型告訴我們,各個線程會將共享變量從主內(nèi)存中拷貝到工作內(nèi)存,然后執(zhí)

行引擎會基于工作內(nèi)存中的數(shù)據(jù)進行操作處理。線程在工作內(nèi)存進行操作

后何時會寫到主內(nèi)存中?這個時機對普通變量是沒有規(guī)定的,而針對

volatile修飾的變量給java虛擬機特殊的約定,線程對volatile變量的修

改會立刻被其他線程所感知,即不會出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象,從而保證數(shù)據(jù)

的“可見性"。簡單說:被volatile修飾的變量能夠保證每個線程能夠獲

取該變量的最新值,從而避免出現(xiàn)數(shù)據(jù)臟讀的現(xiàn)象。

?保證了不同線程對這個變量進行操作時的可見性。(實現(xiàn)可見性)

即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。

?禁止進行指令重排序。(實現(xiàn)有序性)

?volatile只能保證對單次讀/寫的原子性。i++這種操作不能保證原子

性。(實現(xiàn)原子性)

?雙檢驗方式

publicclassL?碎兔醺]£必9(

privatestaticLazySingletoninstance=null;

privateLazySingletonO[}

publicstaticLazySingletongetJn?tanceO(

if(instance==null){

//該方法是靜態(tài)方法,俘在方法區(qū)并且整個JVM只有一份,所以要加類鎖

synchronized(LazySingleton.class^I

instance=newLazySingletonO,

)

)

returninstance

-)

由于JVM編譯器的指令重排機制,同樣會出現(xiàn)問題。

?問題:當進行LazySingletoninstance=newLazySingleton。;時,JVM

會進行下面的1,2,3操作,其中2,3的操作JI順序可能顛倒。

線程0線程1

時間

判斷instance是否為null

線程1初次訪問對象

?反射攻擊解決方案

在Java語言中,不僅可以通過new關(guān)鍵字直接創(chuàng)建對象,還可以通過反射機制創(chuàng)

建對象。即使訪問權(quán)限是private,也可以通過setAccessible。來啟動和禁用訪問

安全檢查的開關(guān),參數(shù)值為true則指示反射的對象在使用時應該取消Java語言訪

問檢查。

?問題示例

publicstaticvc-i'in)ain(String[]args)throwsException(

Constructor<LazySingleton>c=LazySingleton.class.getDeclaredConstructor0

〃號數(shù)值為true則指示反射的對象由史用時應該取消Java譜存訪問檢在

c.setAccessible(true),

LazySingletoninstancel=c.newInstanceO:

LazySingletoninstance?=LazySingleton.gefJnsranceO,

System.?ut.println(instancel)

System,our.println(instance2)

System,out.println(instancel=instance?):

)

MainTestmain。

MainTest

D:\Java\jdkl.8.0_211\bin\java.exe

com.jr.demo.LazySingleton042110106

com.jr.demo.LazySingleton0531d:2ca

false

?解決方案

publicclassLazySingleton(

privatestaticLazySingletoninstance=null.

privatestaticbooleanflag=false;

pflvaieLazySlngleionO(

if(flag){//避免第?次創(chuàng)建實例的是:反射

thrownewRuntimeException'<'''/::nY匚;;'):

)

if(instance1=null){

thrownewRuntimeExceptionC'.;>iiih'-1d;

)

flag=true;

J__________________________________________________________________

publicstaticLazySingletongetlnstanceO(

在私有構(gòu)造器里面進行判斷,如果instance對象已經(jīng)實例化了,就拋出異常。

但是如果先通過反射創(chuàng)建一個對象,創(chuàng)建后的instance還是null,這個時候通

過getlnstanceO獲取instance對象會創(chuàng)建第二個。所以還要通過設(shè)置標志位

的方式來解決這個問題。

?序列化破壞解決方案

?序列化破壞單例模式示例

publicstaticvoidmain(String口args)throwsException{

LazySingletoninstancel=LazySingleton.getlnstance()

//使用序列化和反序列化創(chuàng)建實例

ByteArrayOutputStrearobos=newByteArrayOutputStrearn0.

ObjectOutputStreamoos=newObjectOutputStream(bos)

oos.writeObject(instancel)

byte[]bytes=bos.toByteArrayO

ByteArrayInputStreambis=newByteArrayinputStream(bytes):

ObjectInputStreamois=newObjectlnputStream(bis)

LazySingletoninstance?=(LazySingleton)ois.readObject0,

System,out.printIn(instancel).

System,out.printIn(instance?);

System,out.printIn(instancel=instance2)|

)

MainTestmainO

MainTest

D:\Java\jdkl.8.0_211\bin\java.exe...

com.jr.demo.LazySingleton@255316f2

com.jr.demo.LazySingleton?142d9b6e

false

序列化和反序列化操作時,每次反序列化一個序列化的實例時,都會創(chuàng)建一個

新的實例。

?解決方案

想將單例類變成可序列化的,僅在聲明上加上implementsSerializable是不

夠的,為了維護并保證單例,必須聲明所有實例域都是瞬時(transient)的,

并提供一個readResolve方法。

?1.實現(xiàn)Serializable接口;

單例類變成可序列化,必須實現(xiàn)序列化接口;

?2.使用transient聲明實例域;

序列化形式并不需要包含任何實際的數(shù)據(jù),所有的實例域都應該被聲明為

瞬時的。如果依賴readResolve進行實例控制,帶有對引用類型的所有實

例域必須transient,否則,就有可能在readResolve方法被運行之前,

保護指向反序列化對象的引用。

?3.提供一個readResolve方法;

對于一個正在被反序列化的對象,如果它的類定義了一個readResolve方

法,那么在反序列化之后,新建對象上的readResolve方法就會被調(diào)用,

然后該方法返回的對象引用將被返回,取代新建的對象,新建對象的引用

不需要再被保留,因此立即成為垃圾回收的對象。

?代碼示例

?單例類

.icclassLazySingletoniiuplementsSerializable{

privatestatic]transient]azySingletoninstance=null.

privatestaticbooleanflag=false:

privateObjectreadResolveO{

returninstance.

?測試類

puLlistati-vc-i.]main<String[]args)throwException{

LazySingletoninstancel=LazySingleton.getInstanceO.

11使用序列化和反序列化創(chuàng)建實例

ByteArrayOutputStreambos=newByteArrayOutputStream()

ObjectOutputStreamoos=newObjectOutputStreeun(bos).

oos.writeObject(instancel)

byte口bytes=bos.toByteArray()

ByteArrayinputStreambis=newByteArrayinputStream(bytes);

ObjectInputStreamois=newObjectInputStream(bis).

LazySingletoninstance2=(LazySingleton)ois.readObject0

System,out.println(instancel)

System,out.println(instance2).

System,cut.printIn(instancel=instance?):

)

MainTestmain。

MainTest

D:\Java\jdkl.8.0_211\bin\java.exe...

com.jr.demo.LazySingleton@255316f2

com.jr.demo.LazySingleton@255316f2

true

?方式二:餓漢式

類加載的時候就創(chuàng)建一個實例,在調(diào)用getlnstance方法之前實例已經(jīng)存在了。無線

程安全問題。

?static特性

?static變量在類裝載的時候進行初始化

?多個實例的static變量會共享同一塊內(nèi)存區(qū)域

?代碼示例

publicclass曬1電丫顯舉]士[Qp{

privatestaticfinalHungrySing1etoninstance=newHungrySingletonO

privateHungrySingletonO[}

publicstaticHungrySingletongetInstanggO(

returninstance,

)

)

?反射攻擊解決方案

publicclassHungrySing1eton(

privatestaticfinalHungrySing1etoninstance=newHungrySingleton0;

Iprivate"HungfySingleton(?t

if(instance1=null){

thrownewRuntimeException(「飛鳥造*;1/TJ!!):|

)

|}|

publicstaticHungrySing1etongetlnstanceO{

returninstance;

)}

私有構(gòu)造函數(shù)增加判斷。

?序列化破壞解決方案

?1.實現(xiàn)Serializable接口;

單例類變成可序列化,必須實現(xiàn)序列化接口;

?2.使用transient聲明實例域;

序列化形式并不需要包含任何實際的數(shù)據(jù),所有的實例域都應該被聲明為瞬時

的。如果依賴readResolve進行實例控制,帶有對引用類型的所有實例域必

須transient,否則,就有可能在readResolve方法被運行之前,保護指向

反序列化對象的引用。

?3.提供一個readResolve方法;

對于一個正在被反序列化的對象,如果它的類定義了一個readResolve方法,

那么在反序列化之后,新建對象上的readResolve方法就會被調(diào)用,然后該方

法返回的對象引用將被返回,取代新建的對象,新建對象的引用不需要再被保

留,因此立即成為垃圾回收的對象。

?方式三:靜態(tài)內(nèi)部類

餓漢式不能實現(xiàn)延遲加載,始終占據(jù)內(nèi)存;而懶漢式線程安全控制繁瑣麻煩,而且性能

也會受到影響。靜態(tài)內(nèi)部類單例方式,能夠?qū)⒍叩娜秉c克服而兼顧優(yōu)點,即可以做

到延遲加載,且線程安全。

?靜態(tài)內(nèi)部類的屬性

?由static修飾的成員式內(nèi)部類,它的對象與外部類對象不發(fā)生依賴關(guān)系,其相

當于其外部類的成員。

?外部類初次加載,會初始化靜態(tài)變量、靜態(tài)代碼塊、靜態(tài)方法,但不會加載內(nèi)

部類和靜態(tài)內(nèi)部類。

?靜態(tài)內(nèi)部類只有被調(diào)用時才會被加載,從而實現(xiàn)了延遲加載。

?代碼示例

?靜態(tài)內(nèi)部類

publicclass§1旦!icJnncrClassSjneleiQn(

privateStaticInnerClassSingletonO0

privatestaticclassInnerClass(

privatestaticStaticInnerClassSingleton:nsrance=newStaticInnerClassSingletonO

)

publicstat?<:StaticInnerClassSingleton工(

returnInnerClass.instance

)

)

當?shù)谝淮握{(diào)用getlnstance()時,它第一次讀取InnerClass.instance,導致

InnerClass內(nèi)部類得到初始化,而這個類在裝載并被初始化的時候,會初始化

它的靜態(tài)域。從而創(chuàng)建了StaticInnerClassSingleton的實例,由于是靜態(tài)的

屬性,因此只會在虛擬機裝載類的時候初始化一次,并由JVM來保證它的線程

安全性。

?反射攻擊解決方案

?私有構(gòu)造函數(shù)判空

publicclassSti.以段.式]as$SieIQR(

privateStaticInnerdlassSingletonOt

Iif(InnerClass.instance1=null)(

thrownewRuntineExceptiond'例構(gòu)巨器力I—;I

)I))I

privatestaticclassInnerClass(

privatestaticStaticInnerClassSingletoninstance=newStaticInnerClassSingletonO.

)}

publicstaticStaticInnerClassSingletongetlmceO(

returnInnerClass.instance.

)

)

?序列化破壞解決方案

與懶漢式一致

?1.實現(xiàn)Serializable接口;

單例類變成可序列化,必須實現(xiàn)序列化接口;

?2.使用transient聲明實例域;

序列化形式并不需要包含任何實際的數(shù)據(jù),所有的實例域都應該被聲明為

瞬時的。如果依賴readResolve進行實例控制,帶有對引用類型的所有實

例域必須transient,否則,就有可能在readResolve方法被運行之前,

保護指向反序列化對象的引用。

?3.提供一個readResolve方法;

對于一個正在被反序列化的對象,如果它的類定義了一個readResolve方

法,那么在反序列化之后,新建對象上的readResolve方法就會被調(diào)用,

然后該方法返回的對象引用將被返回,取代新建的對象,新建對象的引用

不需要再被保留,因此立即成為垃圾回收的對象。

?方式四:枚舉

《effectiveJava》第三版中提到:單元素枚舉類型經(jīng)常成為實現(xiàn)Singleton的最住

方法.使用枚舉來實現(xiàn)單例模式會非常簡潔,而且提供了序列化的機制,并由JVM從

根本上提供保障,絕對防止多次實例化,并且天然線程安全,是更簡潔,高效,安全的

方式

?枚舉類特性

?使用enum定義的枚舉類默認繼承了java.lang.Enum類,因此不能再繼承其

他類;

?枚舉類的構(gòu)造器只能使用private權(quán)限修飾符;

?枚舉類的所有實例必須在枚舉類中顯式列出,分隔;結(jié)尾)。列出的實例系統(tǒng)會

自動添加publicstaticfinal修飾;

?必須在枚舉類的第一行聲明枚舉類對象;

?枚舉類常用方法

?values():返回枚舉類型的對象數(shù)組。該方法可以很方便地遍歷所有的枚舉值。

?valueOf(Stringstr):可以把一個字符串轉(zhuǎn)為對應的枚舉類對象。要求字符串

必須是枚舉類對象的"名字"。如不是,會有運行時異常:

IllegalArgumentExceptiono

?toString():返回當前枚舉類對象常量的名稱。

?代碼示例

?單元素枚舉

publicenuinEnumlnstance{

INSTANCE,

privateUseruser;

privateEnumlnstance0{

user=newUser0;

user.setName(,jack');

I)

publicUsergetUser0{

returnuser;

I)

publicstaticEnumlnstancegetlnstanceO(

returnINSTANCE-

I)

)

?測試

publicjtaiicvoidmain(Siring口args)throwsException(

Enumlnstanceinstance】=Enumlnstance.getlnsfanceO

Enunilnstanceinstance2=Enumlnstance.getlnstanceO

System,our.printlnt|+(instancel.getUser()==instance2.getUserO))

//序列化破壞

ByteArrayOutputStreambos=newByleArrayOutputStrejjtnO,

ObjectOutputStreamoos=newObjectOutputStream(bos)

oos.writeObject(instancel),

byte[]bytes=bos.toByteArray(),

ByteArraylnputStreambis=newByteArraylnputStream(bytes)

ObjectInputStreamois=newObjectInputStream(bis),

Enumlnstanceinstance3=(Enumlnstance)ois.readObject()

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論