小學生學習設計模式之單例模式

編碼小學生發表於2020-04-06

小學生學習設計模式之單例模式

一、什麼是單例模式

單例模式(Singleton Pattern),涉及一個單一的類,該類負責建立自己的物件,確保只有單個物件建立,提供一種訪問其唯一的物件的方式,可以直接訪問,不需要例項該類的物件。

  • 單例類只有一個例項,外部不能例項單例類,構造方法隱藏
  • 單例類必須自己建立自己的唯一例項
  • 單例類必須提供這一例項

二、實現

image-20200405175034864
SingletonPatterm
public class Singleton {

    /**
     * 唯一例項
     */

    private static Singleton instance = new Singleton();

    /**
     * 構造方法隱藏,無法例項
     */

    private Singleton(){}

    /**
     * 提供外部訪問唯一例項的方法
     * @return
     */

    public static Singleton getInstance(){
        return instance;
    }

    public void message(){
        System.out.println("This is Singleton Patterm");
    }
}
複製程式碼
public class SingletonPattermDemo {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton == instance);
        instance.message();
    }
}
/*
* true
* This is Singleton Patterm
*/

複製程式碼

三、單例模式的幾種實現方法

image-20200405175034864
幾種實現方式

1. 懶漢式,執行緒不安全

public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        return instance = instance != null?instance:new Singleton();
    }
}
複製程式碼

2. 懶漢式,執行緒安全

public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
        return instance = instance != null?instance:new Singleton();
    }
}
複製程式碼

3. 餓漢式

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}
複製程式碼

4. 雙檢鎖/雙重校驗鎖(DCL,double-checked locking)

public class Singleton {
    private volatile static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
複製程式碼

5. 靜態內部類

public class Singleton {
    private static class SingletonInstance{
        private static final Singleton instance = new Singleton();
    }
    private Singleton(){}
    public static final Singleton getInstance(){
        return SingletonInstance.instance;
    }
}
複製程式碼

6. 登記式

public class Singleton implements Serializable{
    private static Map<String,Object> map;
    
    static{
        map = new ConcurrentHashMap<>();
        map.put(Singleton.class.getName(),new Singleton());
    }
    private Singleton(){
        System.out.println(Singleton.class.getName());
    }

    public static final Object getInstance(String name){
        if (name == null){
            name = Singleton.class.getName();
        }
        if(map.get(name) == null){
            try {
                map.put(name,Class.forName(name).newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
}
複製程式碼

7. 列舉(推薦)

public enum Singleton{
    INSTANCE;
}
複製程式碼

四、破解單例模式

1. 通過反射破解單例(列舉除外)

public class SingletonPattermDemo {
    public static void main(String[] args)  throws Exception{
     
        Class cla = Class.forName("cn.pzaily.Singleton");
        Constructor constructor = cla.getDeclaredConstructor(null);
        constructor.setAccessible(true);

        Singleton singleton1 = (Singleton) constructor.newInstance();
        Singleton singleton2 = (Singleton) constructor.newInstance();
        System.out.println(singleton1 == singleton2);
    }
}
複製程式碼

解決辦法:

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton(){
        //在構造器中加個邏輯判斷,多次呼叫丟擲異常
        if(instance != null){
            throw new RuntimeException();
        }
    }
    public static final Singleton getInstance(){
        return instance;
    }
}
複製程式碼

2. 通過反序列化機制破解單例(列舉除外)

public class SingletonPattermDemo {
    public static void main(String[] args) throws Exception {
        String path = "xxx";
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        Singleton instance3 = null;

        try(FileOutputStream fileOutputStream = new FileOutputStream(path);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
            FileInputStream fileInputStream = new FileInputStream(path);
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            ){
            objectOutputStream.writeObject(instance1);
            instance3 = (Singleton) objectInputStream.readObject();
        }catch (Exception e){
            e.printStackTrace();
        }
        
        System.out.println(instance1 == instance3);
        System.out.println(instance1 == instance2);
    }
}
複製程式碼

解決辦法

public class Singleton implements Serializable{
    private static final Singleton instance = new Singleton();
    private Singleton(){
        if(instance != null){
            throw new RuntimeException();
        }
    }
    public static final Singleton getInstance(){
        return instance;
    }
    //反序列化定義該方法,則不需要建立新物件
    private Object readResolve() throws ObjectStreamException{
        return instance;
    }
}
複製程式碼

本文使用 mdnice 排版

相關文章