1. 程式人生 > 實用技巧 >設計模式2——單例模式

設計模式2——單例模式

設計模式2——單例模式

餓漢式:

package com.ghl.single;

/**
* @ProjectName DesignPattern
* @ClassName Hungry
* @Date 2020/8/25 15:42
* @Author gaohengli
* @Version 1.0
*/
//餓漢式單例
public class Hungry {

    //可能會浪費空間
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    private byte[] data3=new byte[1024*1024];
    private byte[] data4=new byte[1024*1024];

    //私有化建構函式
    private Hungry() {
    }

    //設立靜態變數,直接建立例項
    private static final Hungry HUNGRY = new Hungry();

    //開放公有方法,判斷是否已存在例項
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

懶漢式:

package com.ghl.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
* @ProjectName DesignPattern
* @ClassName LazyMan
* @Date 2020/8/25 16:01
* @Author gaohengli
* @Version 1.0
*/
//DCL懶漢式單例
public class LazyMan {

    private static boolean flag = false;

    //私有化構造器
    private LazyMan() {
        /*//解決反射破壞單例的問題
        synchronized (LazyMan.class) {
            if (lazyMan != null) {
                throw new RuntimeException("不要試圖用反射破壞異常");
            }
        }*/

        synchronized (LazyMan.class) {
            if (flag==false){
                flag=true;
            }else {
                throw new RuntimeException("不要試圖用反射破壞異常");
            }
        }
    }

    //volatile可見性,禁止指令重排序
    private volatile static LazyMan lazyMan;

    //雙重檢測鎖模式的懶漢式單例,DCL懶漢式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是一個原子性操作
                    /*
                    1.分配記憶體空間
                    2.執行構造方法,初始化物件
                    3.把這個物件執行這個空間
                     */
                }
            }
        }
        return lazyMan;
    }

    /*//多執行緒併發
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }*/

    //反射,會破壞單例
    public static void main(String[] args) throws Exception {
        //LazyMan instance = LazyMan.getInstance();

        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//破壞私有許可權

        LazyMan instance = declaredConstructor.newInstance();

        flag.set(instance,false);

        LazyMan instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);
    }
}

靜態內部類:

package com.ghl.single;

/**
* @ProjectName DesignPattern
* @ClassName Holder
* @Date 2020/8/25 17:10
* @Author gaohengli
* @Version 1.0
*/
//靜態內部類實現
public class Holder {

    private Holder(){

    }

    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    //靜態內部類
    public static class InnerClass{
        private static final Holder HOLDER=new Holder();
    }
}

*前面的單例模式都不安全,因為反射可以破解,因此下面使用列舉。

列舉單例模式:

package com.ghl.single;


import java.lang.reflect.Constructor;


/**
* @ProjectName DesignPattern
* @ClassName EnumSingle
* @Date 2020/8/25 17:35
* @Author gaohengli
* @Version 1.0
*/
//enum,列舉,本身也是一個Class類
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}


class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;

        //Exception in thread "main" java.lang.NoSuchMethodException: com.ghl.single.EnumSingle.<init>()
        //Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);

        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);
    }
}

檢測列舉時結果會報Exception in thread "main" java.lang.NoSuchMethodException。出現這個異常的原因是因為EnumSingleton.class.getDeclaredConstructors()獲取所有構造器,會發現並沒有我們所設定的無參構造器,只有一個引數為(String.class,int.class)構造器,而且在反射在通過newInstance建立物件時,會檢查該類是否ENUM修飾,如果是則丟擲異常,反射失敗。所以列舉是不怕發射攻擊的。

列舉有有參建構函式。列舉不怕反射的攻擊。