單例模式是一種對象建立模式,用於生產對象的一個具體實例,它能夠確保系統中一個類只產生一個實例。該模式能帶來兩大好處: java
1. 對於頻繁使用的對象,能夠省略建立對象所花費的時間。 多線程
2. 因爲new的次數減小,於是對系統內存的使用頻率也會下降,這將減輕GC壓力、縮短gc停頓時間 函數
單例模式的核心,在於經過一個接口返回惟一的對象實例。一個簡單的單例實現: 工具
public class Singleton { private Singleton(){ System.out.println("create a Singleton"); } private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } }首先,單例必須有一個private訪問級別的構造函數,只有這樣,才能確保單例不會在系統中的其餘代碼內被實例化;其次,instance成員變量和getInstance()的方法必須是static的
這種實現方式很簡單而且仍是十分可靠的,它惟一的不足就是沒法對instance進行延遲加載,實現延遲的代碼爲 性能
public class LazySingleton { private LazySingleton(){ System.out.println("LazySingleton is create"); } private static LazySingleton instance = null; public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
首先,對於靜態變量instance初始值賦予null,確保系統啓動時沒有額外的負載;
其次,get方法必須是同步的,不然在多線程請擴下,可能會屢次建立!
可是因爲引入了同步,其性能會和第一種狀況差了不少,因此這種下降了系統性能的作法,是有點不可取的,因此咱們對其進行改進:
public class StaticSingleton { private StaticSingleton(){ System.out.println("create a StaticSingleton"); } private static class SingletonHolder{ private static StaticSingleton instance = new StaticSingleton(); } public static StaticSingleton getInstance(){ return SingletonHolder.instance; } }
這個實現中,單例模式使用內部類來維護實例,當StaticSingleton被加載時,其內部類不會初始化,故能夠在調用getInstance()方法調用,纔會加載SingletonHolder.同時,因爲實例的創建實在類加載時完成,故天生對多線程友好
一般狀況下上面的代碼能夠確保系統中惟一實例。可是仍有意外狀況,好比利用反射,這種狀況咱們不討論,咱們討論一種合理的方法:
public class SerSingleton implements Serializable { String name; private SerSingleton(){ System.out.println("create a SerSingleton"); name = "SerSingleton"; } private static SerSingleton instance = new SerSingleton(); public static SerSingleton getInstance(){ return instance; } public static void createString(){ System.out.println("createString in Singleton"); } // private Object readResolve(){ // return instance; // } }
// TODO Auto-generated method stub SerSingleton s1 = null; SerSingleton s = SerSingleton.getInstance(); //先將實例串行化到文件 FileOutputStream fos = new FileOutputStream("s.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s); oos.flush(); oos.close(); //從文件讀出 FileInputStream fis = new FileInputStream("s.txt"); ObjectInputStream ois = new ObjectInputStream(fis); s1= (SerSingleton) ois.readObject(); // Assert.assertEquals(s,s1); System.out.println(s.equals(s1));
若是在第一段代碼中去掉readResovle(),那麼測試代碼(通過串行化和反串行化)後,s和s1指向了不一樣的實例;加上以後便解決該問題。
即便構造函數是私有的,可序列化工具依然能夠經過特殊的途徑去建立類的一個新的實例。序列化操做提供了一個很特別的鉤子(hook)-類中具備一個私有的被實例化的方法readresolve(),這個方法能夠確保類的開發人員在序列化將會返回怎樣的object上具備發言權。足夠奇怪的,readresolve()並非靜態的,可是在序列化建立實例的時候被引用。