設計模式十二————單例模式

單例模式定義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
實現手段:將構造方法私有化,在類外不能創建對象。只能通過類內的靜態方法創建對象。第一次訪問這個靜態方法時,創建一個對象,並將對象的引用返回;第二次訪問這個對象時直接將引用返回。

code…


單例類:

public class Singleton {
    private static Singleton singleton=null;
    private Singleton(){}
    public static Singleton createObject()
    {
        if(singleton==null)
            singleton=new Singleton();
        return singleton;
    }
}

main方法:

public class Main {
    public static void main(String[] args) {
        Singleton single=Singleton.createObject();
        System.out.println(single);
        Singleton single1=Singleton.createObject();
        System.out.println(single1);
    }
}

運行結果:兩個對象hashcode一樣

com.Singleton.Singleton@74a14482
com.Singleton.Singleton@74a14482

先將單例類內的引用懸空,每次訪問靜態方法時先判斷引用是否爲空,如果爲空則表明時第一次訪問,創建一個對象讓引用指向並返回;如果不爲空,則表明不是第一次訪問,直接將引用返回即可。
缺點:在多線程中,如果多個線程同時訪問這個靜態方法,如果引用爲null的判斷都成立,那麼每個線程都會創建一個實例。如下:

public class Main {
    static class thread implements Runnable
    {
        @Override public void run() {
            Singleton singleton=Singleton.createObject();
            System.out.println(singleton);
        }
    }
    public static void main(String[] args) {
        Thread thread1=new Thread(new thread());
        Thread thread2=new Thread(new thread());
        Thread thread3=new Thread(new thread());
        Thread thread4=new Thread(new thread());
        Thread thread5=new Thread(new thread());
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

由於程序運行較快,難以模擬多個線程同時啓動,因此在if條件中使線程休眠1s:

 if(singleton==null)
 {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        singleton=new Singleton();
 }

運行結果出現五個不用的對象,單例模式被破壞。

com.Singleton.Singleton@60cb300b
com.Singleton.Singleton@dbefca
com.Singleton.Singleton@63158364
com.Singleton.Singleton@e4c242a
com.Singleton.Singleton@6fec3797

解決方法:

  • 對方法進行同步,用synchronized來修飾方法。
  • 在靜態塊中創建對象,並賦值給引用
第二種方法代碼:
public class Singleton {
    private static Singleton singleton=null;
    static {
        singleton=new Singleton();
    }
    private Singleton(){}
    public static Singleton createObject()
    {
        return singleton;
    }
}

因爲類只加載一次,對應對象創建一次。所以在靜態塊中創建對象。
破解方法:可以通過反射來破解。無論用什麼方法寫的單例模式,都可以通過反射來破解。
反射中最簡單的破解方法,得到單例的Class對象,得到構造方法,然後實例化。

public class Main {
    public static void main(String[] args)throws Exception{
        Class single=Class.forName("com.Singleton.Singleton");
        Constructor constructor=single.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton singleton1=(Singleton) constructor.newInstance();
        Singleton singleton2=(Singleton) constructor.newInstance();
        Singleton singleton3=(Singleton) constructor.newInstance();
        Singleton singleton4=(Singleton) constructor.newInstance();
        System.out.println(singleton1);
        System.out.println(singleton2);
        System.out.println(singleton3);
        System.out.println(singleton4);
    }
}

這樣得到四個不同的對象。


上一篇
---The End---
下一篇
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章