前言
在剛學程式設計沒多久就聽說過設計模式的大名,不過由於當時還是個徹徹底底的菜鳥,並沒有去觸碰。直到在開始工作中對簡單的業務程式碼較為熟悉之後,才正式的接觸設計模式。當時最早接觸的設計模式是工廠模式,不過本文講的是單例模式,這裡就留著下篇文章中在講解。至於為什麼先講解單例模式? 那是因為單例模式是設計模式中最簡單的... 。凡事總有個先後順序,所以就先易後難了。好了,廢話不多說了,開始進入正片。
設計模式簡介
說明:這裡說了的簡介就是真的 “簡介”。
什麼是設計模式
設計模式是一套被反覆使用的、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。
為什麼使用設計模式
使用設計模式是為了重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。
設計模式型別
設計模式有23種型別。按照主要分類可以分為三大類:
一、建立型模式
這些設計模式提供了一種在建立物件的同時隱藏建立邏輯的方式,而不是使用 new運算子直接例項化物件。這使得程式在判斷針對某個給定例項需要建立哪些物件時更加靈活。
- 單例模式
- 工廠模式
- 抽象工廠模式
- 建造者模式
- 原型模式
二、結構型模式
這些設計模式關注類和物件的組合。繼承的概念被用來組合介面和定義組合物件獲得新功能的方式。
- 介面卡模式
- 橋接模式
- 過濾器模式
- 組合模式
- 裝飾器模式
- 外觀模式
- 享元模式
- 代理模式
三、行為型模式
這些設計模式特別關注物件之間的通訊。
- 責任鏈模式
- 命令模式
- 直譯器模式
- 迭代器模式
- 中介者模式
- 備忘錄模式
- 觀察者模式
- 狀態模式
- 空物件模式
- 策略模式
- 模板模式
- 訪問者模式
設計模式的原則
設計模式的六大原則
- 開閉原則:對擴充套件開放,對修改關閉。
- 里氏代換原則:對開閉原則的補充。任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟體單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。
- 依賴倒轉原則:針對介面程式設計,依賴於抽象而不依賴於具體。
- 介面隔離原則:儘量使用多個隔離的介面,為了降低類之間的耦合度。
- 迪米特法則:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。
- 合成複用原則:儘量使用合成/聚合的方式,而不是使用繼承。
單例模式
什麼是單例模式
保證一個系統中的某個類只有一個例項而且該例項易於外界訪問。例如Windows介面的工作管理員就可以看做是一個單例。
單例模式的使用場景
在程式中比較常用的是資料庫連線池、執行緒池、日誌物件等等。
單例模式使用
最早我們在學習單例模式的時候,基本都會接觸這兩種模式:餓漢式和飽漢式(懶漢式)。 那我們先來看看這兩個模式的實現。
餓漢式 定義一個私有的構造方法,並將自身的例項物件設定為一個私有屬性,並加上static和final修飾符,然後通過公共的靜態方法呼叫返回例項。
class SingletonTest1 {
private SingletonTest1() {
}
private static final SingletonTest1 instance = new SingletonTest1();
public static SingletonTest1 getInstance() {
return instance;
}
}
複製程式碼
飽漢式 定義一個私有的構造方法,定義一個該類靜態私有的變數,然後定義一個公共的靜態方法,對該類的值進行空判斷,不為空直接返回,否則重新構建一個。
class SingletonTest2 {
private SingletonTest2() {
}
private static SingletonTest2 instance;
public static SingletonTest2 getInstance() {
if (instance == null) {
instance = new SingletonTest2();
}
return instance;
}
}
複製程式碼
簡單的介紹了這兩種的模式,然後我們再來看看這兩種模式的優缺點吧。 餓漢式
- 優點:寫起來很簡單,並且不會因為不加synchronized關鍵字而造成的執行緒不安全問題。
- 缺點:當該類被載入的時候,會初始化該例項和靜態變數並被建立並分配記憶體空間,並且會一直佔用記憶體。
飽漢式
- 優點:寫起來很簡單,在第一次呼叫的時候才會初始化,節省了記憶體。
- 缺點:執行緒不安全,多個執行緒呼叫可能會出現多個例項。
- 總結:書寫簡單,執行緒不安全,效率還行。
雖然 飽漢式可以通過加上synchronized關鍵字保證執行緒安全。但是效率方法來說還不說是最優。
這裡在介紹下個人認為在JDK1.5之前最優的兩種寫法,一種是靜態內部類,另一種是雙重鎖檢查。
靜態內部類 定義一個私有的構造方法,定義一個該類私有靜態的內部類,然後在內部類中定義一個該類的靜態變數,然後通過公共的final修飾的靜態方法呼叫返回例項。
class SingletonTest4 {
private SingletonTest4(){
}
private static class SingletonTest5{
private static SingletonTest4 instance = new SingletonTest4();
}
public static final SingletonTest4 getInstance(){
return SingletonTest5.instance;
}
}
複製程式碼
因為該類的內部類是私有的,除了對外公佈的公共靜態方法getInstance(),是無法訪問的。因為它是延遲載入,所以讀取讀取例項的時候不會進行同步,幾乎沒有效能的缺陷,而且還是執行緒安全的,並且不依賴JDK的版本。
雙重鎖檢查 定義一個私有構造方法,通過volatile定義靜態私有變數,保證了該變數的可見性,然後定義一個共有的靜態方法,第一次對該物件例項化時與否判斷,不為空直接返回,提升效率;然後使用synchronized 進行同步程式碼塊,防止物件未初始化時,在多執行緒訪問該物件在第一次建立後,再次重複的被建立;然後第二次對該物件例項化時與否判斷,如果未初始化,則初始化,否則直接返回該例項。
class SingletonTest6 {
private SingletonTest6() {
}
private static volatile SingletonTest6 instance;
public static SingletonTest6 getIstance() {
if (instance == null) {
synchronized (SingletonTest6.class) {
if (instance == null) {
instance = new SingletonTest6();
}
}
}
return instance;
}
}
複製程式碼
這種模式在很長的一段時間內可以說是最優的了,記憶體佔用低,效率高,執行緒安全,多執行緒操作原子性。但是有個缺點就是書寫麻煩,對新手不太友好。
JDK1.5之後出現了列舉,並且完美支援單例模式,並且執行緒安全、效率高!但是這些不是最重要的,最重要的是書寫超級簡單!究竟有多簡單,看下面的示例應該就可以瞭解一下了。。。
列舉單例
enum SingletonTest7{
INSTANCE;
}
複製程式碼
對的,你沒看錯,就這點程式碼,其它不需要了。。。 列舉需要在JDK1.5之後的版本,它無償提供序列化機制,絕對防止多次例項化,即使在面對複雜的序列化或者反射攻擊的時候。這種方法也被Effective Java作者Josh Bloch 所提倡。
總結
單例模式的幾種使用就到這了,那麼我們來總結下使用單例模式需要注意什麼(不包括列舉)。
- 構造方法私有化(private);
- 定義一個私有(private)靜態(static)例項化物件;
- 對外提供一個公共(public)靜態(static)的方法得到該例項;
原創不易,如果感覺不錯,希望給個推薦!您的支援是我寫作的最大動力! 版權宣告: 作者:虛無境 部落格園出處:http://www.cnblogs.com/xuwujing CSDN出處:http://blog.csdn.net/qazwsxpcm 個人部落格出處:http://www.panchengming.com