[英]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.