簡體   English   中英

單例公共靜態最終

[英]singleton public static final

我一直想知道Java中的單例。 按照慣例,單例設置如下:

private static MyClass instance = null;
public static MyClass getInstance(){
    if (instance == null){
        instance = new MyClass();
    }
    return instance;
}
private MyClass(){}

最近,我切換到使用以下內容:

public static final MyClass instance = new MyClass();
private MyClass(){}

由於沒有空檢查,因此它更短,更快捷,並且鍵入MyClass.instance對我來說比鍵入MyClass.getInstance()更好。 有什么理由不能使第二種方法成為主流呢?

第一個版本在第一次實際需要時創建實例,而第二個版本(較短的版本)在初始化類后立即運行構造函數

類或接口類型T將在以下任何一種首次出現之前立即初始化:

  • T是一個類,並創建T的實例。
  • T是一個類,並調用T聲明的靜態方法。
  • 分配由T聲明的靜態字段。
  • 使用由T聲明的靜態字段,並且該字段不是常量變量(第4.12.4節)。
  • T是頂級類(第7.6節),並執行詞法嵌套在T中的斷言(第14.10節)(第8.1.3節)。 [...]

調用類Class和包java.lang.reflect中的某些反射方法也會導致類或接口初始化。

如果構造函數中的代碼進行了昂貴的操作,則首次使用時的初始化會提高性能,從而可以加快應用程序的啟動速度。 另一方面,第二個版本易於閱讀,並且自動具有線程安全性。

無論如何,最新技術不會以任何方式創建單例:對於一堆KB,您可以獲得依賴注入庫,使其適合您,並處理更復雜的場景(例如,查看Spring和AOP支持的注入)。 )。

注意:第一個版本在粘貼的代碼段中不是線程安全的

您首先描述的方式稱為延遲實例化,即僅在首次調用對象時才創建該對象。 此方法不是線程安全的,因為第二個線程可能創建第二個實例。

如果您閱讀以下書籍:

Joshua Bloch撰寫的有效Java

他解釋說,單例模式的最佳實現是通過使用Enum

public enum Singleton {

  INSTANCE;

  public void doSomething() {
     ...
  }

}

然后,您將通過Enum調用您的單身人士,如下所示:

public class Test {

    public void test(){
        Singleton.INSTANCE.doSomething();
    }
}

這與您所說的內容非常吻合,因為它看起來更好,更短,但也保證永遠不會出現第二個實例。

我可以想到兩個原因:

第一個是封裝 :在類暴露給客戶端代碼之后,您可能會對如何以及何時初始化單例進行第二次思考。 初始化方法為您提供了以后更改策略的更多自由。 例如,您可能改變主意,並根據運行時另一個靜態變量的值決定使用兩個不同的構造函數,而不是一個。 使用您的解決方案,您必須在將類加載到內存時僅使用一個構造函數,而使用getInstance()可以更改初始化邏輯,而不影響客戶端代碼的接口。

第二個是惰性初始化 :使用常規的單例實現,僅當客戶端代碼首次需要時, MyClass對象才加載到內存中。 而且,如果根本不需要客戶端代碼,則可以節省應用程序分配的內存。 請注意,在程序運行之前,可能不確定是否需要單身人士。 例如,它可能取決於用戶與程序的交互。

但是,惰性初始化不是您可能需要的東西。 例如,如果您正在編寫一個交互式系統,並且單例的初始化很耗時,那么在加載程序時而不是在用戶已經在與之交互時,初始化它實際上可能會更好,因為后者可能會導致首次調用getInstance()時系統響應中的延遲。 但是在這種情況下,您可以使用public方法初始化實例,如下所示:

private static MyClass instance = getInstance();

同步線程的最佳方法是使用Double Checked(確保一次僅一個線程將進入同步塊,並避免在每次執行代碼時獲得鎖)。

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private attribute of this class
        private String searchWord = "";
        private String[] list = new String[]{"Stack", "Overflow"};

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

反射:可能導致反射破壞單例類的單例屬性,如以下示例所示:

 // Java code to explain effect of Reflection 

 import java.lang.reflect.Constructor; 

 // Singleton class 
 class Singleton  
 { 
     // public instance initialized when loading the class 
     public static Singleton instance = new Singleton(); 

     private Singleton()  
     { 
         // private constructor 
     } 
 } 

 public class GFG  
 { 

     public static void main(String[] args) 
     { 
         Singleton instance1 = Singleton.instance; 
         Singleton instance2 = null; 
         try
         { 
             Constructor[] constructors =  
                     Singleton.class.getDeclaredConstructors(); 
             for (Constructor constructor : constructors)  
             { 
                 // Below code will destroy the singleton pattern 
                 constructor.setAccessible(true); 
                 instance2 = (Singleton) constructor.newInstance(); 
                 break; 
             } 
         } 

         catch (Exception e)  
         { 
             e.printStackTrace(); 
         } 

     System.out.println("instance1.hashCode():- " 
                                       + instance1.hashCode()); //366712642
     System.out.println("instance2.hashCode():- " 
                                       + instance2.hashCode()); //1829164700
     } 
 } 


暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM