I walk very slowly, but I never walk backwards
設計模式 - 單例模式(下)
? 寂然
大家好~片排,我是寂然凿蒜,本節(jié)課呢,我們接著來聊單例模式显设,本節(jié)課的重點是單例模式最后兩種寫法,靜態(tài)內部類和枚舉藏畅,接著帶大家閱讀JDK源碼中單例模式的應用敷硅,以及對單例模式的注意事項進行總結,那我們啟程吧
靜態(tài)內部類
通過靜態(tài)內部類愉阎,同樣可以實現(xiàn)單例模式绞蹦,首先大家要對靜態(tài)內部類有了解, 用static修飾的內部類榜旦,稱為靜態(tài)內部類幽七, 我們先編寫代碼,驗證其正確性溅呢,然后對靜態(tài)內部類的寫法進行分析澡屡,示例代碼如下:
// 單例模式 - 靜態(tài)內部類
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);
}
}
寫法分析
靜態(tài)內部類的特點是,當Singleton類進行類加載的時候咐旧,靜態(tài)內部類是不會被加載的
當調用Singleton類的 getInstance() 方法驶鹉,用到了 SingletonInstance 的靜態(tài)變量的時候,會導致靜態(tài)內部類SingletonInstance 進行類加載铣墨,當然類加載的過程中室埋,線程是安全的,所以這種寫法不會出現(xiàn)線程安全問題
這種方式采用類加載的機制來保證初始化實例時只有一個線程伊约, 類的靜態(tài)屬性只會在第一次加載類的時候初始化姚淆,所以在這里,JVM 幫助我們保證了線程的安全性屡律,在類進行初始化時腌逢,別的線程是無法進入的 ,避免了線程不安全超埋,利用靜態(tài)內部類特點實現(xiàn)延遲加載搏讶,效率也較高,所以這種方式也是推薦使用的
枚舉方式
通過枚舉的方式霍殴,其實也可以實現(xiàn)單例模式窍蓝,這是單例模式的第八種寫法,示例代碼如下
//單例模式 - 枚舉方式
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 中添加的枚舉來實現(xiàn)單例模式繁成,不僅能避免多線程同步問題吓笙,而且還能防止反序列化重新創(chuàng)建新的對象,這種方式是 Effective Java 作者 Josh Bloch 提倡的方式巾腕,在實際開發(fā)中面睛,同樣推薦使用這種方式
JDK源碼分析
JDK 中絮蒿,java.lang.Runtime 就是經典的餓漢式單例模式,我們寫一段測試代碼叁鉴,然后進行源碼分析
public class Test {
public static void main(String[] args) {
//得到一些系統(tǒng)信息
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); //處理器個數(shù)
}
}
通過源碼大家可以看到土涝,Runtime 就是經典的餓漢式寫法,首先Runtime類 java 中肯定會用到幌墓,不存在浪費但壮,其次,餓漢式的寫法常侣,類的加載過程中創(chuàng)建對象蜡饵,避免了線程安全問題
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,單例模式保證了系統(tǒng)內存中該類只存在一個對象胳施,節(jié)省了系統(tǒng)資源溯祸,對于一些需要頻繁創(chuàng)建銷毀的對象, 使用單例模式可以提高系統(tǒng)性能
2舞肆,當想實例化一個單例類的時候焦辅,必須要記住使用相應的獲取對象的方法,而不是使用 new
單例模式使用場景
- 需要頻繁的進行創(chuàng)建和銷毀的對象
- 創(chuàng)建對象時耗時過多或耗費資源過多(即:重量級對象)
- 經常用到的對象
- 工具類對象
- 頻繁訪問數(shù)據庫或文件的對象(比如數(shù)據源椿胯、session 工廠等)
下節(jié)預告
OK筷登,到這里,單例模式就正式完結了哩盲,我們從單例模式的八種寫法入手前方,對每一種進行利弊分析,著重講解了雙重檢查機制种冬,最后镣丑,我們看了JDK源碼中單例模式的使用舔糖,以及給大家強調了單例模式的注意事項娱两,涉及的內容相對比較完整全面,下一節(jié)金吗,我們進入第二個設計模式 - 工廠模式的學習十兢,最后,希望大家在學習的過程中摇庙,能夠感覺到設計模式的有趣之處旱物,高效而愉快的學習,那我們下期見~