1. 程式人生 > >如何破壞單例模式?如何防止?

如何破壞單例模式?如何防止?

前幾天看到一道面試題,問的是單例的模式書寫以及單例模式能否被破壞?如何破壞?如何防止?

對於第一問單例模式的書寫,瞭解過設計模式的同學對單例模式絕對不會陌生。常用的單例模式有懶漢式、餓漢式兩種情況。實際的應用場景也是很常見,好比如資料庫連線池的設計,還有Windows的Task Manager(工作管理員)。

單例模式的書寫

這裡列出懶漢式的寫法

/**在多執行緒下如何保證單例模式需要記住三點。
  *第一就是雙重加鎖機制。
  *第二是確保SingleTest為volatile變數。
  *第三,是我覺得最容易被遺忘的一點,就是建構函式為private。
*/
public class
SingleTest {
public static volatile SingleTest singleTest ; private SingleTest(){ } public static SingleTest getSingleTest(){ if(singleTest==null){ synchronized(SingleTest.class){ if(singleTest==null){ singleTest = new SingleTest(); } } } return
singleTest; } }

單例模式的破壞

上面的寫法看起來沒問題,但是存在安全問題。因為我們可以通過反射破壞單例模式。

public static void main(String[] args) throws Exception{

        Constructor constructor = SingleTest.class.getDeclaredConstructor();
        constructor.setAccessible(true);

        SingleTest s1 = SingleTest.getSingleTest
(); SingleTest s2 = SingleTest.getSingleTest(); SingleTest s3 = (SingleTest) constructor.newInstance(); System.out.println("輸出結果為:"+s1.hashCode()+"," +s2.hashCode()+","+s3.hashCode()); }
輸出結果為:1286131032,1286131032,1581347769

從輸出的結果我們就可以看出s1和s2為同一物件,s3為新物件。s3是我們通過反射機制,進而呼叫了私有的建構函式,然後產生了一個新的物件。

誒,既然單例模式存在漏洞,在JDK中那些應用單例模式的物件不是就會被惡意破壞了嘛。當時我也這樣想的,我嘗試了一下去破壞Runtime(在JDK為單例模式物件)。

public static void main(String[] args) throws Exception{

    Constructor constructor1 = Runtime.class.getConstructor();
    Runtime r1 = (Runtime) constructor1.newInstance();
    Runtime r2 = Runtime.getRuntime();
    Runtime r3 = Runtime.getRuntime();

    System.out.println(r1.hashCode());
    System.out.println(r2.hashCode());
    System.out.println(r3.hashCode());
}

結果是被異常丟擲,檢視原始碼就看得出,JDK對已經封裝好的單例物件是有保護機制的。

防止單例模式的破壞

那麼我們如何去保護我們自定義的單例物件呢?很簡單,只需要修私有的建構函式即可。

private static boolean flag = false;

private SingleTest(){
        synchronized(SingleTest.class){
            if(flag == false){
                flag = !flag;
            }else {
                throw new RuntimeException("單例模式被侵犯!");
            }
        }
 }