創建型模式【1】——單例模式

https://blog.csdn.net/qq_40794973/article/details/104165984 

  • 定義 : 保證一個類僅有一個實例,並提供一個全局訪問點
  • 類型 : 創建型
  • 適用場景 : 想確保任何情況下都絕對只有一個實例
  • 優點 : 在內存裏只有一個實例,減少了內存開銷;可以避免對資源的多重佔用;設置全局訪問點,嚴格控制訪問
  • 缺點 : 沒有接口,擴展困難
  • 重點 : 私有構造器;線程安全;延遲加載;序列化和反序列化安全;反射

1 懶漢式(線程不安全)[不可用] 

在多線程模式下,懶漢式由於線程不安全是不可用的

public class Singleton {
    private static Singleton instance;
    //私有化構造器
    private Singleton() {
        try {
            //創建對象 耗時操作
            TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        } catch (InterruptedException ignored) {}
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
@Test
public void testThreadVis() throws IOException {
    Runnable task = () -> {
        Singleton instance = Singleton.getInstance();
        System.out.println(Thread.currentThread().getName() + "  " + instance);
    };
    new Thread(task, "線程一").start(); //線程一  Singleton@46385f83
    new Thread(task, "線程二").start(); //線程二  Singleton@4ef99cce
    System.in.read();
}

反序列化破壞單例模式

/**
 * 餓漢式(靜態常量)
 */
//這個單例類實現了序列化接口
public class Singleton implements Serializable {
    private final static Singleton instance = new Singleton();
    // 構造函數私有化
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}
//反序列化破壞單例模式
@Test
public void test() throws IOException, ClassNotFoundException {
    Singleton instance = Singleton.getInstance();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
    oos.writeObject(instance);
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
    Singleton deserializationObj = (Singleton) ois.readObject();
    System.out.println(instance); // Singleton@5e8c92f4
    System.out.println(deserializationObj); // Singleton@244038d0
}

3 readResolve 避免反序列化

/**
 * 餓漢式(靜態常量)
 */
//這個單例類實現了序列化接口
public class Singleton implements Serializable {
    private final static Singleton instance = new Singleton();
    // 構造函數私有化
    private Singleton() { }
    public static Singleton getInstance() {
        return instance;
    }
    //
    private Object readResolve(){
        return instance;
    }
}
//聲明readResolve方法,防止反序列化破壞單例模式
@Test
public void test() throws IOException, ClassNotFoundException {
    Singleton instance = Singleton.getInstance();
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
    oos.writeObject(instance);
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
    Singleton deserializationObj = (Singleton) ois.readObject(); //打斷點
    System.out.println(instance); // Singleton@5e8c92f4
    System.out.println(deserializationObj); // Singleton@5e8c92f4
    System.out.println(instance == deserializationObj);
}
//java.io.ObjectInputStream#readObject
public final Object readObject() {
    if (enableOverride) {
        return readObjectOverride();
    }
    // if nested read, passHandle contains handle of enclosing object
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(false); //打斷點
//java.io.ObjectInputStream#readObject0
private Object readObject0(boolean unshared) throws IOException {
    try {
        switch (tc) {
            case TC_OBJECT:
                return checkResolve(readOrdinaryObject(unshared)); //打斷點
//java.io.ObjectStreamClass#invokeReadResolve
private Object readOrdinaryObject(boolean unshared) {
    Object obj;
    try {
        //創建對象
        obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {}
    if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
        //反射調用readResolve方法
        Object rep = desc.invokeReadResolve(obj);
        if (unshared && rep.getClass().isArray()) {
            rep = cloneArray(rep);
        }
        if (rep != obj) {
            // Filter the replacement object
            if (rep != null) {
                if (rep.getClass().isArray()) {
                    filterCheck(rep.getClass(), Array.getLength(rep));
                } else {
                    filterCheck(rep.getClass(), -1);
                }
            }
            handles.setObject(passHandle, obj = rep);
        }
    }
    return obj;
}

4 反射破壞單例模式

public class Singleton {
    private final static Singleton instance = new Singleton();
    // 構造函數私有化 反射可以破壞
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
}
@Test
public void test() throws Exception {
    Singleton instance = Singleton.getInstance();
    Class<Singleton> objClass = Singleton.class;
    Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
    constructor.setAccessible(true); //java.lang.IllegalAccessException: Class SingletonTest can not access a member of class Singleton with modifiers "private"
    Singleton reflexObj = constructor.newInstance();
    //
    System.out.println(instance); //Singleton@32a1bec0
    System.out.println(reflexObj); //Singleton@22927a81
    System.out.println(reflexObj == instance); //false
}

5 反射防禦

public class Singleton {
    private final static Singleton instance = new Singleton();
    // 構造函數私有化
    private Singleton() {
        //反射防禦 只有餓漢式、靜態內部類 纔可用
        if (instance != null) {
            throw new RuntimeException("單例構造器禁止反射調用");
        }
    }
    public static Singleton getInstance() {
        return instance;
    }
}
public class Singleton {
    private Singleton() {
        if (SingletonInstance.INSTANCE != null) {
            throw new RuntimeException("單例構造器禁止反射調用");
        }
    }
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

反射防禦失敗

/**
 * 懶漢式(線程安全)(不推薦)
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {
        if (instance != null) {
            throw new RuntimeException("單例構造器禁止反射調用");
        }
    }
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
@Test
public void test() throws Exception {
    Class<Singleton> objClass = Singleton.class;
    Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    //如果反射方法先執行,依然是會有兩個不同的對象的
    Singleton reflexObj = constructor.newInstance();
    Singleton instance = Singleton.getInstance();
    System.out.println(instance); // Singleton@32a1bec0
    System.out.println(reflexObj); // Singleton@22927a81
    System.out.println(reflexObj == instance); //false
}

可能你想到用可以變量來標記私有化構造器執行了幾次,然後在決定是否拋出異常,但是反射既然能執行私有化構造器那麼也能直接該變你的標記值,對於lazy的單例模式反射破壞單例是無法有效防禦的

public class Singleton {
    private static Singleton instance;
    private static boolean flag = true;
    private Singleton() {
        //在複雜的邏輯都是卵的
        if (flag) {
            flag = false;
        } else {
            throw new RuntimeException("單例構造器禁止反射調用");
        }
    }
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
//通過反射來破壞lazy類型的單例模式,是無法有效避免的
@Test
public void test() throws Exception {
    Singleton instance = Singleton.getInstance(); //
    Class<Singleton> objClass = Singleton.class;
    Constructor<Singleton> constructor = objClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    //修改字段的值
    Field flag = objClass.getDeclaredField("flag");
    flag.setAccessible(true);
    flag.set(instance, true);
    Singleton reflexObj = constructor.newInstance();
    System.out.println(reflexObj); //Singleton@22927a81
    System.out.println(instance); //Singleton@78e03bb5
    System.out.println(reflexObj == instance); //false
}

7 完美的單例模式

7.1 枚舉能夠防止序列化破壞

public enum EnInstance {
    //
    INSTANCE;
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    public static EnInstance getInstance() {
        return INSTANCE;
    }
}
@Test
public void test() throws IOException, ClassNotFoundException {
    //反序列化
    EnInstance instance = EnInstance.getInstance();
    //
    instance.setData(new Date());
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
    oos.writeObject(instance);
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton_file")));
    EnInstance deserializationObj = (EnInstance) ois.readObject();
    System.out.println(instance.getData()); // Fri May 22 11:15:50 CST 2020
    System.out.println(deserializationObj.getData()); // Fri May 22 11:15:50 CST 2020
    System.out.println(instance == deserializationObj);//true
}
//java.io.ObjectInputStream#readEnum
private Enum<?> readEnum(boolean unshared) throws IOException {
    String name = readString(false);
    Enum<?> result = null;
    Class<?> cl = desc.forClass();
    if (cl != null) {
        try {
            //拿到的是唯一的
            Enum<?> en = Enum.valueOf((Class)cl, name);

7.2 枚舉能夠防止反射破壞

//java.lang.Enum
//枚舉只有唯一的構造器
//protected Enum(String name, int ordinal) {
//    this.name = name;
//    this.ordinal = ordinal;
//}
@Test
public void test() throws Exception {
    Class<EnInstance> objClass = EnInstance.class;
    Constructor<EnInstance> constructor = objClass.getDeclaredConstructor(); // 無法獲取到無參構造器 java.lang.NoSuchMethodException: EnInstance.<init>()
}
//java.lang.Enum
//枚舉只有唯一的構造器
//protected Enum(String name, int ordinal) {
//    this.name = name;
//    this.ordinal = ordinal;
//}
@Test
public void test() throws Exception {
    Class<EnInstance> objClass = EnInstance.class;
    Constructor<EnInstance> constructor = objClass.getDeclaredConstructor(String.class, int.class);
    constructor.setAccessible(true);
    EnInstance reflexObj = constructor.newInstance("喬碧蘿", 58); //java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    //java.lang.reflect.Constructor.newInstance
    //public T newInstance(Object ... initargs) {
    //	//如果是枚舉是不允許反射創建對象的
    //	if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    //		throw new IllegalArgumentException("Cannot reflectively create enum objects");
}

https://varaneckas.com/jad/

//jad EnInstance.class
public final class EnInstance extends Enum {
    public static EnInstance[] values(){
        return (EnInstance[])$VALUES.clone();
    }
    public static EnInstance valueOf(String name){
        return (EnInstance)Enum.valueOf(EnInstance, name);
    }
    //構造器私有
    private EnInstance(String s, int i){
        super(s, i);
    }
    public Object getData(){
        return data;
    }
    public void setData(Object data){
        this.data = data;
    }
    public static EnInstance getInstance(){
        return INSTANCE;
    }
    //靜態類變量
    public static final EnInstance INSTANCE;
    private Object data;
    private static final EnInstance $VALUES[];
    static 
    {
        INSTANCE = new EnInstance("INSTANCE", 0);
        $VALUES = (new EnInstance[] {
            INSTANCE
        });
    }
}

 

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