I walk very slowly, but I never walk backwards
設計模式 - 單例模式(下)
寂然
大家好~,我是寂然,本節課呢,我們接着來聊單例模式,本節課的重點是單例模式最後兩種寫法,靜态内部類和枚舉,接着帶大家閱讀JDK源碼中單例模式的應用,以及對單例模式的注意事項進行總結,那我們啟程吧
靜态内部類
通過靜态内部類,同樣可以實作單例模式,首先大家要對靜态内部類有了解, 用static修飾的内部類,稱為靜态内部類, 我們先編寫代碼,驗證其正确性,然後對靜态内部類的寫法進行分析,示例代碼如下:
// 單例模式 - 靜态内部類
class Singleton{
private Singleton(){
}
private static class SingletonInstance {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
寫法分析
靜态内部類的特點是,當Singleton類進行類加載的時候,靜态内部類是不會被加載的
當調用Singleton類的 getInstance() 方法,用到了 SingletonInstance 的靜态變量的時候,會導緻靜态内部類SingletonInstance 進行類加載,當然類加載的過程中,線程是安全的,是以這種寫法不會出現線程安全問題
這種方式采用類加載的機制來保證初始化執行個體時隻有一個線程, 類的靜态屬性隻會在第一次加載類的時候初始化,是以在這裡,JVM 幫助我們保證了線程的安全性,在類進行初始化時,别的線程是無法進入的 ,避免了線程不安全,利用靜态内部類特點實作延遲加載,效率也較高,是以這種方式也是推薦使用的
枚舉方式
通過枚舉的方式,其實也可以實作單例模式,這是單例模式的第八種寫法,示例代碼如下
//單例模式 - 枚舉方式
enum Singleton{
INSTANCE; //屬性
public void method(){
System.out.println("執行個體方法的列印");
}
}
public class EnumDemo {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1);
instance.method();
}
}
這借助 JDK1.5 中添加的枚舉來實作單例模式,不僅能避免多線程同步問題,而且還能防止反序列化重新建立新的對象,這種方式是 Effective Java 作者 Josh Bloch 提倡的方式,在實際開發中,同樣推薦使用這種方式
JDK源碼分析
JDK 中,java.lang.Runtime 就是經典的餓漢式單例模式,我們寫一段測試代碼,然後進行源碼分析
public class Test {
public static void main(String[] args) {
//得到一些系統資訊
Runtime runtime = Runtime.getRuntime();
int processors = runtime.availableProcessors();
long freeMemory = runtime.freeMemory();
long maxMemory = runtime.maxMemory();
System.out.println("freeMemory " + freeMemory); //空閑記憶體
System.out.println("maxMemory " + maxMemory); //最大記憶體
System.out.println("processors " + processors); //處理器個數
}
}
通過源碼大家可以看到,Runtime 就是經典的餓漢式寫法,首先Runtime類 java 中肯定會用到,不存在浪費,其次,餓漢式的寫法,類的加載過程中建立對象,避免了線程安全問題
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
注意事項
1,單例模式保證了系統記憶體中該類隻存在一個對象,節省了系統資源,對于一些需要頻繁建立銷毀的對象, 使用單例模式可以提高系統性能
2,當想執行個體化一個單例類的時候,必須要記住使用相應的擷取對象的方法,而不是使用 new
單例模式使用場景
- 需要頻繁的進行建立和銷毀的對象
- 建立對象時耗時過多或耗費資源過多(即:重量級對象)
- 經常用到的對象
- 工具類對象
- 頻繁通路資料庫或檔案的對象(比如資料源、session 工廠等)
下節預告
OK,到這裡,單例模式就正式完結了,我們從單例模式的八種寫法入手,對每一種進行利弊分析,着重講解了雙重檢查機制,最後,我們看了JDK源碼中單例模式的使用,以及給大家強調了單例模式的注意事項,涉及的内容相對比較完整全面,下一節,我們進入第二個設計模式 - 工廠模式的學習,最後,希望大家在學習的過程中,能夠感覺到設計模式的有趣之處,高效而愉快的學習,那我們下期見~