標籤:
問題1.效率問題:如果不需要用到單例類的對象,如何保證單例類對象不會被建立;
問題2.安全執行緒:如何保證擷取到的對象是單例的。
1. 一般單例類的寫法有:
class Singleton{ private static Singleton instance = null;
private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
註:這麼寫僅在單線程中保證單個執行個體。
2. 可以加同步解決以安全執行緒問題,但是效率相對較低
class Singleton{ private static Singleton instance = null;
private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}
3.其實也有另外的寫法:
class Singleton{ private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public static int i = 3; public static void test() { System.out.println("test invoked."); }}
註:這樣解決了同步的問題,但是當靜態test()方法被調用或者靜態變數i被調用,即使你不需要使用單例對象,他也已經被載入到了記憶體中了。
4.推薦的寫法如下:使用內部類方式,JVM保證在使用到內部類時才去初始化其成員變數,並保證安全執行緒。
class Singleton{ private Singleton() { } private static class SingletonHolder { public static Singleton instance = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }
補充:
java類載入過程:類什麼時候會初始化。
一個java檔案從被載入到被卸載這個生命過程,總共要經曆四個階段:
載入->連結(驗證+準備+解析)->初始化(使用前的準備)->使用->卸載
什麼時候要對類進行初始化工作, jvm有嚴格的規定(四種情況):
1.遇到new,getstatic,putstatic,invokestatic這4條位元組碼指令時,加入類還沒進行初始化,則馬上對其進行初始化工作。其實就是3種情況:用new執行個體化一個類時、讀取或者設定類的靜態欄位時(不包括被final修飾的靜態欄位,因為他們在類編譯的時候已經被塞進常量池了)、以及執行靜態方法的時候。
2.使用java.lang.reflect.*的方法對類進行反射調用的時候,如果類還沒有進行過初始化,馬上對其進行。
3.初始化一個類的時候,如果他的父親還沒有被初始化,則先去初始化其父親。
4.當jvm啟動時,使用者需要指定一個要執行的主類(包含static void main(String[] args)的那個類),則jvm會先去初始化這個類。
以上4種預先處理稱為對一個類進行主動的引用,其餘的其他情況,稱為被動引用,都不會觸發類的初始化。
Java單例模式 注意的問題