反射和内省_单例设计模式–内省和最佳实践
反射和內省
定義:
Singleton是“ 四人幫”設計模式的一部分,它屬于創新設計模式。 在本文中,我們將更深入地研究Singleton模式的用法。 就建模而言,它是最簡單的設計模式之一,但另一方面,就使用的復雜性而言,這也是最有爭議的模式之一。
在Java中,Singleton模式將確保在Java虛擬機中僅創建一個類的實例。 它用于提供對對象的全局訪問點。 在實際使用方面,Singleton模式用于日志記錄,緩存,線程池,配置設置,設備驅動程序對象。
設計模式通常與工廠設計模式結合使用。 此模式還用于Service Locator JEE模式。
結構體:
單例類圖
- 靜態成員:包含單例類的實例。
- 私有構造函數:這將阻止其他人實例化Singleton類。
- 靜態公共方法:此方法提供對Singleton對象的全局訪問點,并將實例返回給客戶端調用類。
實現示例:延遲初始化
讓我們看一下Java中的單例實現示例。 下面的代碼使用惰性初始化過程。
public class SingletonExample {// Static member holds only one instance of the// SingletonExample classprivate static SingletonExample singletonInstance;// SingletonExample prevents any other class from instantiatingprivate SingletonExample() {}// Providing Global point of accesspublic static SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();}return singletonInstance;}public void printSingleton(){System.out.println('Inside print Singleton');} }單例模式代碼說明
當使用SingletonExample.getSingletonInstance()。printSingleton()從客戶端調用此類時。 那么在第一次時只會創建一個實例。 在第二次以后的所有后續調用中,我們將引用相同的對象,并且getSingletonInstance()方法返回在第一次創建時所用的SingletonExample類的相同實例。 您可以通過在以下語句中添加打印語句來對此進行測試:
public static SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();System.out.println('Creating new instance');}return singletonInstance;}如果現在我們從客戶端類中調用Singleton類為:
SingletonExample.getSingletonInstance().printSingleton(); SingletonExample.getSingletonInstance().printSingleton(); SingletonExample.getSingletonInstance().printSingleton(); SingletonExample.getSingletonInstance().printSingleton();上面的調用的輸出是:
Creating new instance Inside print Singleton Inside print Singleton Inside print Singleton Inside print Singleton實施示例:雙重檢查鎖定
上面的代碼在單線程環境中絕對可以正常工作,并且由于延遲初始化,因此可以更快地處理結果。 但是,以上代碼可能會在多線程環境中的結果中產生一些突然的行為,因為在這種情況下,如果多個線程嘗試同時訪問getSingletonInstance()方法,則它們可能會創建同一SingletonExample類的多個實例。 想象一個實際的情況,我們必須創建一個日志文件并對其進行更新,或者在使用諸如打印機之類的共享資源時進行更新。 為避免這種情況,我們必須使用某種鎖定機制,以便第二個線程在第一個線程完成該過程之前不能使用此getInstance()方法。
堆中的單例
在圖3中,我們顯示了多個線程如何訪問單例實例。 由于單例實例是存儲在堆的PermGen空間中的靜態類變量。 這同樣適用于getSingletonInstance()實例方法,因為它也是靜態的。 在多線程環境中,為了防止每個線程創建單例對象的另一個實例并因此導致并發問題,我們將需要使用鎖定機制。 這可以通過synced關鍵字實現。 通過使用此synced關鍵字,我們可以防止Thread2或Thread3訪問getSingletonInstance()方法內部的Thread1時的單例實例。
從代碼角度來看,這意味著:
public static synchronized SingletonExample getSingletonInstance() {if (null == singletonInstance) {singletonInstance = new SingletonExample();}return singletonInstance;}因此,這意味著每次調用getSingletonInstance()都會給我們帶來額外的開銷。 為了防止執行此昂貴的操作,我們將使用雙重檢查鎖定 ,以使同步僅在第一次調用期間發生,并且將此昂貴的操作限制為僅執行一次。 僅在以下情況才需要:
singletonInstance = new SingletonExample();代碼示例:
public static volatile SingletonExample getSingletonInstance() {if (null == singletonInstance) {synchronized (SingletonExample.class){if (null == singletonInstance) {singletonInstance = new SingletonExample();}}}return singletonInstance;}在上面的代碼片段中,假設有多個線程并發并嘗試創建新實例。 在這種情況下,可能有三個或更多線程在同步塊上等待訪問。 由于我們使用了同步,因此只有一個線程可以訪問。 當第一個線程退出該塊時,將等待所有在同步塊上等待的其余線程。 但是,當剩余的并發線程進入同步塊時,由于雙重檢查:空檢查,它們被阻止進一步進入。 由于第一個線程已經創建了一個實例,因此沒有其他線程會進入此循環。
其余所有不幸運地與第一個線程一起進入同步塊的線程將在第一個null檢查時被阻塞。 這種機制稱為雙重檢查鎖定 ,它提供了顯著的性能優勢,并且是具有成本效益的解決方案。
實施示例:易失性關鍵字
我們還可以在實例變量聲明中使用volatile關鍵字。
private volatile static SingletonExample singletonInstance;volatile關鍵字在多線程環境中用作并發控制工具,并以最準確的方式提供最新更新。但是請注意,雙重檢查鎖定可能在Java 5之前不起作用。在這種情況下,我們可以使用早期加載機制。 如果我們還記得原始的示例代碼,則使用了延遲加載。 如果提早加載,我們將在開始時實例化SingletonExample類,并將其引用到私有靜態實例字段。
public class SingletonExample {private static final SingletonExample singletonInstance = new SingletonExample;// SingletonExample prevents any other class from instantiatingprivate SingletonExample() {}// Providing Global point of accesspublic static SingletonExample getSingletonInstance() {return singletonInstance;}public void printSingleton(){System.out.println('Inside print Singleton');} }在這種方法中,單例對象是在需要之前創建的。 JVM負責靜態變量的初始化,并確保進程是線程安全的,并確保在線程嘗試訪問它之前創建實例。 在延遲加載的情況下,當客戶端類調用getSingleInstance()而在早期加載的情況下,是singletonInstance創建當類在存儲器中加載的singletonInstance被創建。
實現示例:使用枚舉
使用Enum在Java 5或更高版本中實現Singleton:
Enum是線程安全的,并且通過Enum實現Singleton可以確保您的Singleton即使在多線程環境中也只有一個實例。 讓我們看一個簡單的實現:
常見問題解答:
問題:為什么我們不能使用靜態類而不是單例呢?
回答:
- 與靜態類相比,單例的主要優勢之一是它可以實現接口并擴展類,而靜態類則不能(它可以擴展類,但不繼承其實例成員)。 如果我們考慮靜態類,那么它只能是嵌套的靜態類,因為頂級類不能是靜態類。 靜態意味著它屬于它所在的類,而不屬于任何實例。 因此,它不能是頂級課程。
- 另一個區別是,靜態類僅與Singleton不同,其所有成員都將是靜態的。
- Singleton的另一個優點是它可以延遲加載,而static每次首次加載時都會初始化。
- 單例對象存儲在堆中,而靜態對象存儲在堆棧中。
- 我們可以克隆Singleton對象,但是不能克隆靜態類對象。
- Singleton可以使用多態的面向對象功能,但是靜態類不能。
問題:單例類可以被子類化嗎?
答:坦率地說,單例只是一種設計模式,可以將其子類化。 但是,有必要了解子類化單例類背后的邏輯或要求,因為子類可能不會通過擴展Singleton類來繼承單例模式目標。 但是,可以通過在類聲明中使用final關鍵字來防止子類化。
問題:使用克隆是否可以有多個單例實例?
答:這是一個很好的收獲! 我們現在干什么? 為了防止創建單例實例的另一個實例,我們可以從clone()方法內部引發異常。
問題:如果我們使用序列化和反序列化創建另一個單例實例,將會產生什么影響?
答:當我們序列化一個類并反序列化它時,它將創建單例類的另一個實例。 基本上,您對單例實例進行反序列化的次數將創建多個實例。 那么在這種情況下,最好的方法是將單例設為枚舉。 這樣,底層的Java實現即可處理所有細節。 如果這不可能,那么我們將需要重寫readobject()方法以返回相同的單例實例。
問題:Singleton可以使用哪種其他模式?
答:還有其他一些模式,例如Factory方法,構建器和原型模式,它們在實現過程中使用Singleton模式。
問題:JDK中的哪些類使用單例模式?
答案:java.lang.Runtime:在每個Java應用程序中,只有一個Runtime實例,允許該應用程序與其運行的環境進行交互。 getRuntime等效于單例類的getInstance()方法。
Singleton設計模式的用途:
單例模式的各種用法:
- 硬件接口訪問:單例的使用取決于要求。 但是,在需要外部硬件資源使用限制的情況下,實際上可以使用單例,例如,可以將打印后臺處理程序做成單例的硬件打印機,以避免多個并發訪問并產生死鎖。
- 記錄器:同樣,單例是在日志文件生成中使用的很好的潛在候選者。 想象一個應用程序,其中日志記錄實用程序必須根據從用戶收到的消息來生成一個日志文件。 如果存在多個使用此日志記錄實用程序類的客戶端應用程序,則它們可能會創建此類的多個實例,并且在并發訪問同一記錄器文件期間可能會引起問題。 我們可以將logger實用程序類用作單例,并提供全局參考。
- 配置文件:這是單例模式的另一個潛在候選者,因為它具有性能優勢,因為它可以防止多個用戶重復訪問和讀取配置文件或屬性文件。 它創建配置文件的單個實例,該實例可通過多個調用并發訪問,因為它將提供加載到內存對象中的靜態配置數據。 該應用程序僅在第一次讀取配置文件,然后從第二次調用開始,客戶端應用程序從內存對象中讀取數據。
- 緩存:我們可以將緩存用作單例對象,因為它可以具有全局參考點,并且對于將來對緩存對象的所有調用,客戶端應用程序都將使用內存中對象。
動手:
讓我們以一個小的實際示例來更詳細地了解Singleton設計模式。
問題陳述:
設計一個小型的ATM打印應用程序,它可以生成多種類型的交易對帳單,包括迷你對帳單,明細對帳單等。但是,客戶應注意這些對帳單的創建。 確保將內存消耗降至最低。 設計方案: 可以使用四個設計模式的兩個核心Gang(工廠設計模式和Singleton設計模式)滿足上述要求。 為了為ATM機中的ATM事務生成多種類型的語句,我們可以創建一個Statement Factory對象,該對象具有工廠方法createStatements() 。 createStatement將創建DetailedStatement或MiniStatement對象。 客戶端對象將完全不知道對象的創建,因為它僅與Factory接口交互。 我們還將創建一個名為StatementType的接口。 這將允許添加其他對帳單類型對象,例如信用卡對帳單等。 因此,該解決方案遵循面向對象的“打開/關閉”設計原則,具有可擴展性和可擴展性。 減少內存消耗的第二個要求可以通過使用Singleton設計模式來實現。 不需要多次啟動Statement Factory類,并且一個工廠可以創建多個語句對象。 單例模式將創建StatementFactory類的單個實例,從而節省內存。
ATM范例
- 工廠:工廠是抽象類,是客戶的單點聯系。 所有具體的工廠類都需要實現抽象工廠方法。
- StatementFactory:這是Factory創建類,它由creator方法組成。 該類從Factory抽象類擴展。這是所有產品(例如Statements)的主要創建者類。
- StatementType:這是一個產品接口,它提供對需要由Factory類創建的各種類型的產品的抽象。
- DetailedStatement:這是StatementType接口的具體實現,它將打印詳細的語句。
- MiniStatement:這是StatementType接口的具體實現,它將打印迷你語句。
- 客戶端:這是客戶端類,它將調用StatementFactory和StatementType并請求創建對象。
假設:
該設計解決方案僅適用于一臺ATM機。
樣例代碼:
工廠.java
public abstract class Factory {protected abstract StatementType createStatements(String selection);}StatementFactory.java
public class StatementFactory extends Factory {private static StatementFactory uniqueInstance;private StatementFactory() {}public static StatementFactory getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new StatementFactory();System.out.println('Creating a new StatementFactory instance');}return uniqueInstance;}public StatementType createStatements(String selection) {if (selection.equalsIgnoreCase('detailedStmt')) {return new DetailedStatement();} else if (selection.equalsIgnoreCase('miniStmt')) {return new MiniStatement();}throw new IllegalArgumentException('Selection doesnot exist');} }StatementType.java
public interface StatementType {String print(); }DetailedStatement.java
public class DetailedStatement implements StatementType {@Overridepublic String print() {System.out.println('Detailed Statement Created');return 'detailedStmt';} }MiniStatement.java
public class MiniStatement implements StatementType {@Overridepublic String print() {System.out.println('Mini Statement Created');return 'miniStmt';} }客戶端程序
public class Client {public static void main(String[] args) {System.out.println('Enter your selection:');BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String selection = null;try {selection = br.readLine();} catch (IOException e) {e.printStackTrace();}Factory factory = StatementFactory.getUniqueInstance();StatementType objStmtType = factory.createStatements(selection);System.out.println(objStmtType.print());}}結論:
在以上文章中,我們詳細介紹了Singleton模式,如何在實際應用中實現Singleton模式。 盡管單例模式看起來很簡單,但是除非有強烈要求,否則我們應該阻止自己使用它。 您可以將結果歸咎于多線程環境中結果的不可預測性。 盡管我們可以在Java 5及更高版本中使用枚舉 ,但有時總是很難在枚舉中實現您的邏輯,否則Java 5之前可能會有遺留代碼。希望我們的讀者喜歡本文。
參考: Singleton設計模式–來自ICG博客上我們JCG合作伙伴 Mainak Goswami 的內省和最佳實踐 。
翻譯自: https://www.javacodegeeks.com/2013/02/singleton-design-pattern-an-introspection-and-best-practices.html
反射和內省
總結
以上是生活随笔為你收集整理的反射和内省_单例设计模式–内省和最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win10系统实现滑动关机的解决方法(图
- 下一篇: 小米note配置(小米Note10配置)