Java單例模式幾種實現方式
在平時的工作、學員的學習以及面試過程中,單例模式作為一種常用的設計模式,會經常被面試官問到,甚至筆試會要求學員現場默寫,下面將會就單例模式的實現思路和幾種常見的實現方式進行簡單的分享。
單例模式,是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個實例。即一個類只有一個對象實例。是最常用到的設計模式之一,熟悉設計模式的朋友對單例模式都不會陌生。一般介紹單例模式的書籍都會提到 餓漢式 和 懶漢式 這兩種實現方式。但是除了這兩種方式,本文還會介紹其他幾種實現單例的方式。
基本的實現思路
單例模式要求類能夠有返回對象一個引用
通俗的講:即設計一個類,在整個應用中只存在一個對象(需要做到讓本類的外部不能夠隨意創建對象)
單例的實現主要是通過以下三個步驟:
① 構造方法私有化(暫時不考慮反射)
② 在內部創建好一個對象並保存起來
③ 向外提供一個公共的靜態的方法,返回內部對象的地址
第一種實現方式:餓漢式[靜態常量]的方式
示例代碼:
package cn.itsource.sington;
/**
* 餓漢式[靜態常量]
*
* @author Administrator
*
*/
public class
/**
* 單例模式 設計一個類 ,讓這個 類中只存在一個對象的實例
*
* 1 需要一個私有的 構造方法,這樣可以避免外部創建對象
*/
private SingletonTest1() {
}
/*
* 2 在本類內部創建一個 對象 ,保存起來
*/
private static SingletonTest1 instance = new SingletonTest1();
/*
* 3 向外公布一個 公共的 靜態的方法 ,返回內部保存的這個對象
*
*/
public static SingletonTest1 getInstance() {
return instance;
}
}
優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。
缺點:對象的創建是類加載的時候,可能會導致類加載很慢,沒有達到Lazy Loading的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。
第二種實現方式:餓漢式[靜態代碼塊]的方式
示例代碼:
package cn.itsource.sington;
public class SingletonTest3 {
private SingletonTest3() {
}
private static SingletonTest3 instance;
static {
instance = new SingletonTest3();
}
public static SingletonTest3 getInstance() {
return instance;
}
}
這種方式和上面的方式其實類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。
第三種實現方式:懶漢式[雙重檢查]的方式
示例代碼:
package cn.itsource.sington;
/**
* 懶漢式
* @author Administrator
*/
public class SingletonTest2 {
/**
* 需求:單例模式 設計一個類 ,讓這個 類中只存在一個對象的實例
*
* 1 需要一個私有的 構造方法,這樣可以避免外部創建對象
*/
private SingletonTest2() {
}
/*
* 2 類加載進內存的時候,對象還沒有存在,只有調用了getInstance()方法時,對
象才開始創建
*/
private static SingletonTest2 instance;
/*
* 3 向外公布一個 公共的 靜態的方法 ,返回內部保存的這個對象
* 懶漢式是延遲加載,如果多個線程同時操作懶漢式時就有可能出現線程安全問題,
解決線程安全問題:
可以加同步來解決。但是加了同步之後,每一次都要比較鎖,效率就變慢了,
所以可以加雙重判斷來提高程序效率。
*/
public static SingletonTest2 getInstance() {
if (instance == null) {
synchronized (SingletonTest2.class) {
if (instance == null) {
instance = new SingletonTest2();
}
}
}
return instance;
}
}
雙重判斷,對於多線程開發者來說不會陌生,如代碼中所示,我們進行了兩次if (instance == null)檢查,這樣就可以保證線程安全了。這樣,實例化代碼只用執行一次,後面再次訪問時,判斷if (instance == null),直接return實例化對象。
優點:線程安全;延遲加載;效率較高。
餓漢式和懶漢式的區別:
1餓漢式是類一加載進內存就創建好了對象;
2懶漢式則是類加載進內存的時候,對象還沒有存在,只有調用了getInstance()方法時,對象才開始創建。
3懶漢式是延遲加載,如果多個線程同時操作懶漢式時就有可能出現線程安全問題,解決線程安全問題可以加同步來解決。但是加了同步之後,每一次都要比較鎖,效率就變慢了,所以可以加雙重判斷來提高程序效率。
第四種實現方式:枚舉的方式
示例代碼:
package cn.itsource.sington;
public enum SingletonTest4 {
INSTANCE;
}
借助JDK1.5中添加的枚舉來實現單例模式。不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。可能是因為枚舉在JDK1.5中才添加,所以在實際項目開發中,很少見人這麽寫過。
優點:寫法簡單這是它最大的優點,其次可以自己處理序列化,是線程安全的
缺點:當想實例化一個單例類的時候,必須要記住使用SingletonTest4.INSTANCE獲取對象的方法,而不是使用new,可能會給其他開發人員造成困擾,特別是看不到源碼的時候。
以上是對單例模式常見的幾種實現方式,在教學和學習過程中的一點簡單的總結,希望對大家學習單例模式有一點點幫助。
Java單例模式幾種實現方式