所謂單例模式就是,某一個類只能有一個實例,實現的核心就是將類的構造函數私有化,只能由該類建立對象,其餘對象就不能調用該類的構造函數,即不能建立對象了。java
如今看一個問題:對象的建立方式有哪幾種?
四種:new 、克隆、序列化、反射。安全
其實上面的說法有點問題,改成:……其餘對象就不能調用該類的構造函數,即不能經過new 來建立對象了。那麼是否還有能夠經過其餘的三種方式建立對象呢,即其餘三種方式會不會破壞單例模式呢?函數
克隆可能對單例模式的破壞spa
由克隆咱們能夠想到原型模式,原型模式就是經過clone方法實現對象的建立的,clone方式是Object方法,每一個對象都有,那我使用一個單例模式類的對象,調用clone方法,再建立一個新的對象了,那豈不是上面說的單例模式失效了。固然答案是否認,某一個對象直接調用clone方法,會拋出異常,即並不能成功克隆一個對象。調用該方法時,必須實現一個Cloneable 接口。這也就是原型模式的實現方式。還有即若是該類實現了cloneable接口,儘管構造函數是私有的,他也能夠建立一個對象。即clone方法是不會調用構造函數的,他是直接從內存中copy內存區域的。因此單例模式的類是不能夠實現cloneable接口的。線程
序列化可能對單例模式的破壞code
一是能夠實現數據的持久化;二是能夠對象數據的遠程傳輸。
若是過該類implements Serializable,那麼就會在反序列化的過程當中再創一個對象。這個問題的解決辦法就是在反序列化時,指定反序化的對象實例。添加以下方法:對象
private static final long serialVersionUID = -3979059770681747301L; private volatile static Singleton singleton; private Object readResolve() { return singleton; }
反射是能夠獲取類的構造函數,再加一行 setAccessible(true);就能夠調用私有的構造函數,建立對象了。那麼防止反射破壞Java單例模式的方法就是:當第二次調用構造函數時拋出異常。代碼以下:接口
private volatile static Singleton1 singleton; private static boolean flag = true; private Singleton1 (){ if(flag){ flag = false; }else{ throw new RuntimeException("單例模式險些被破壞,第二個對象未建立成功"); } }
還有一種方式是經過枚舉實現。內存
下面開始正式講單例模式方式:
最好的就是雙重檢測校檢鎖,不過下面將所有的幾種方式都寫出來。
一、線程不安全:懶漢: (懶漢就是延遲加載)get
package com.txc.singleton; import java.io.Serializable; //線程不安全:懶漢 public class Singleton2 implements Serializable{ /** * */ private static final long serialVersionUID = 2857896192197905033L; private volatile static Singleton2 singleton; private static boolean flag = true; private Singleton2 (){ if(flag){ flag = false; }else{ throw new RuntimeException("單例模式險些被破壞,第二個對象未建立成功"); } } public static Singleton2 getInstance() { if (singleton == null) { singleton = new Singleton2(); } return singleton; } private Object readResolve() { return singleton; } }
二、線程安全:懶漢:
package com.txc.singleton; import java.io.Serializable; //線程不安全:懶漢 public class Singleton3 implements Serializable{ /** * */ private static final long serialVersionUID = 2857896192197905033L; private volatile static Singleton3 singleton; private static boolean flag = true; private Singleton3 (){ if(flag){ flag = false; }else{ throw new RuntimeException("單例模式險些被破壞,第二個對象未建立成功"); } } public static synchronized Singleton3 getInstance() { if (singleton == null) { singleton = new Singleton3(); } return singleton; } private Object readResolve() { return singleton; } }
三、餓漢:(非延遲加載)是線程安全的。
package com.txc.singleton; import java.io.Serializable; //線程不安全:懶漢 public class Singleton4 implements Serializable{ /** * */ private static final long serialVersionUID = 2857896192197905033L; private static Singleton4 singleton= new Singleton4(); private static boolean flag = true; private Singleton4 (){ if(flag){ flag = false; }else{ throw new RuntimeException("單例模式險些被破壞,第二個對象未建立成功"); } } public static Singleton4 getInstance() { return singleton; } private Object readResolve() { return singleton; } }
四、枚舉
public enum Singleton { INSTANCE; }
五、雙重校檢鎖
package com.txc.singleton; import java.io.Serializable; public class Singleton1 implements Serializable{ /** * */ private static final long serialVersionUID = -3979059770681747301L; private volatile static Singleton1 singleton; private static boolean flag = true; private Singleton1 (){ if(flag){ flag = false; }else{ throw new RuntimeException("單例模式險些被破壞,第二個對象未建立成功"); } } public static Singleton1 getSingleton() { if (singleton == null) { synchronized (Singleton1.class) { if (singleton == null) { singleton = new Singleton1(); } } } return singleton; } private Object readResolve() { return singleton; } }
若有錯誤,請指出。