单例设计模式示例
本文是我們名為“ Java設計模式 ”的學院課程的一部分。
 在本課程中,您將深入研究大量的設計模式,并了解如何在Java中實現(xiàn)和利用它們。 您將了解模式如此重要的原因,并了解何時以及如何應用模式中的每一個。 在這里查看 ! 
目錄
1.單例模式 2.如何使用單例模式創(chuàng)建類 3.何時使用Singleton 4.下載源代碼1.單例模式
有時對于某些類來說,只有一個實例很重要。 有許多對象,我們只需要它們的一個實例,如果實例化多個對象,我們將遇到各種問題,例如程序行為不正確,資源過度使用或結果不一致。
您可能只需要一個類的對象,例如,當您創(chuàng)建應用程序的上下文時,或者線程可管理的池,注冊表設置,連接到輸入或輸出控制臺的驅(qū)動程序等。該類型顯然會導致程序不一致。
Singleton模式可確保一個類只有一個實例,并提供對其的全局訪問點。 但是,盡管就類圖而言,單例是最簡單的,因為只有一個類,但其實現(xiàn)卻有些棘手。
圖1
在本課程中,我們將嘗試不同的方法來僅創(chuàng)建類的單個對象,還將了解一種方法比另一種方法更好。
2.如何使用單例模式創(chuàng)建類
創(chuàng)建這種類型的類的方法有很多種,但仍然可以看到一種方法比另一種方法更好。
讓我們從一個簡單的方法開始。
如果提供一個使對象可訪問的全局變量該怎么辦? 例如:
package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {public static SingletonEager sc = new SingletonEager();}眾所周知,一個類的靜態(tài)變量只有一個副本,我們可以應用它。 就目前而言,客戶端代碼使用此sc靜態(tài)變量就可以了。 但是,如果客戶端使用新的運算符,則將有此類的新實例。
要停止在類之外實例化該類,讓我們將該類的構造函數(shù)設為私有。
package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {public static SingletonEager sc = new SingletonEager();private SingletonEager(){}}這行得通嗎? 我想是的。 通過使構造函數(shù)保持私有狀態(tài),其他任何類都不能實例化該類。 獲取此類的唯一方法是使用sc靜態(tài)變量,該變量確保只有一個對象存在。
但是,眾所周知,直接訪問類成員不是一個好主意。 我們將提供一種方法,通過該方法,sc變量將獲得訪問權,而不是直接訪問。
package com.javacodegeeks.patterns.singletonpattern;public class SingletonEager {private static SingletonEager sc = new SingletonEager();private SingletonEager(){}public static SingletonEager getInstance(){return sc;} }因此,這是我們的單例類,可確保僅創(chuàng)建該類的一個對象,即使存在多個請求,也將僅返回相同的實例化對象。
這種方法的一個問題是,一旦將類加載到JVM中,就會立即創(chuàng)建對象。 如果從未請求過該對象,則內(nèi)存中將有一個無用的對象。
在需要時創(chuàng)建對象始終是一種好方法。 因此,我們將在第一個調(diào)用上創(chuàng)建一個對象,然后在其他后續(xù)調(diào)用上返回相同的對象。
package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazy {private static SingletonLazy sc = null;private SingletonLazy(){}public static SingletonLazy getInstance(){if(sc==null){sc = new SingletonLazy();}return sc;} }在getInstance()方法中,我們檢查靜態(tài)變量sc是否為null,然后實例化該對象并將其返回。 因此,在第一次調(diào)用時,如果sc為null,則將創(chuàng)建對象,而在接下來的后續(xù)調(diào)用中,它將返回相同的對象。
這看起來確實不錯,不是嗎? 但是,此代碼將在多線程環(huán)境中失敗。 想象兩個線程同時訪問該類,線程t1首次調(diào)用getInstance()方法,它檢查靜態(tài)變量sc是否為null,然后由于某種原因而被中斷。 另一個線程t2調(diào)用getInstance()方法成功通過了if檢查并實例化了該對象。 然后,線程t1醒來,它還創(chuàng)建了對象。 此時,將有兩個此類。
為了避免這種情況,我們將使用synchronized關鍵字到getInstance()方法。 通過這種方式,我們強制每個線程等待其輪流才能進入該方法。 因此,沒有兩個線程會同時進入該方法。 同步帶有價格,它將降低性能,但是如果對getInstance()方法的調(diào)用不會給您的應用程序造成實質(zhì)性開銷,則請忽略它。 另一個解決方法是轉(zhuǎn)向渴望的實例化方法,如前面的示例所示。
package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazyMultithreaded {private static SingletonLazyMultithreaded sc = null;private SingletonLazyMultithreaded(){}public static synchronized SingletonLazyMultithreaded getInstance(){if(sc==null){sc = new SingletonLazyMultithreaded();}return sc;} }但是,如果要使用同步,則有另一種稱為“雙重檢查鎖定”的技術可以減少同步的使用。 使用雙重檢查鎖定,我們首先檢查是否創(chuàng)建了實例,如果沒有創(chuàng)建,則進行同步。 這樣,我們只同步第一次。
package com.javacodegeeks.patterns.singletonpattern;public class SingletonLazyDoubleCheck {private volatile static SingletonLazyDoubleCheck sc = null;private SingletonLazyDoubleCheck(){}public static SingletonLazyDoubleCheck getInstance(){if(sc==null){synchronized(SingletonLazyDoubleCheck.class){if(sc==null){sc = new SingletonLazyDoubleCheck();} }}return sc;} }除此之外,還有其他打破單例模式的方法。
下面的示例說明如何保護您的類免于被多次實例化。
package com.javacodegeeks.patterns.singletonpattern;import java.io.ObjectStreamException; import java.io.Serializable;public class Singleton implements Serializable{private static final long serialVersionUID = -1093810940935189395L;private static Singleton sc = new Singleton();private Singleton(){if(sc!=null){throw new IllegalStateException("Already created.");}}public static Singleton getInstance(){return sc;}private Object readResolve() throws ObjectStreamException{return sc;}private Object writeReplace() throws ObjectStreamException{return sc;}public Object clone() throws CloneNotSupportedException{throw new CloneNotSupportedException("Singleton, cannot be clonned");}private static Class getClass(String classname) throws ClassNotFoundException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();if(classLoader == null) classLoader = Singleton.class.getClassLoader();return (classLoader.loadClass(classname));}}盡管我們可以使用所有這些技術,但是有一種簡單的方法可以創(chuàng)建單例類。 從JDK 1.5開始,您可以使用枚舉創(chuàng)建單例類。 枚舉常量是隱式靜態(tài)的和最終的,創(chuàng)建后就無法更改其值。
package com.javacodegeeks.patterns.singletonpattern;public class SingletoneEnum {public enum SingleEnum{SINGLETON_ENUM;} }當您嘗試顯式實例化Enum對象時,將出現(xiàn)編譯時錯誤。 當Enum靜態(tài)加載時,它是線程安全的。 Enum中的clone方法是最終的,可確保枚舉常量永遠不會被克隆。 枚舉本質(zhì)上是可序列化的,序列化機制可確保不會因反序列化而創(chuàng)建重復的實例。 也禁止使用反射實例化。 這些確保了枚舉實例不存在超出枚舉常量定義的實例。
3.何時使用Singleton
4.下載源代碼
這是有關Singleton Pattern的課程。 您可以在此處下載源代碼: SingletonPattern-Project
翻譯自: https://www.javacodegeeks.com/2015/09/singleton-design-pattern.html
總結
                            
                        - 上一篇: Android 系统被指存储空间计算存逻
 - 下一篇: Apache Lucene基础教程