01设计模式之--单例模式

目录

单例模式

核心作用:

常见场景:

饿汉式

懒汉式

1. 懒汉式 单线程 ok

2.懒汉式 多线程 no

3.懒汉式   完整的双重检测锁DCL 加 原子性操作

4. 反射破坏单例  

5.解决反射破坏单例   双重检测 升级  三级检测

6.用反射创建对象 破坏三重检测

7. 解决用反射创建 对象 秘钥

8.破坏  两个反射创建 对象 秘钥

单例不安全,使用枚举 

test

 破坏枚举,发现问题

idea欺骗了我们

 源码骗了我们,用了一个有参构造器​


单例模式

核心作用:

        保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常见场景:

  • Windows的任务管理器
  • Windows的回收站
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 网站的计数器一般也会采用单例模式,可以保证同步
  • 数据库连接池的设计一般也是单例模式
  • 在Servlet编程中,每个Servlet也是单例的
  • 在Spring中,每个Bean默认就是单例的
  • ……

饿汉式

/**
 * @author gh  Email:@2495140780qq.com
 * @Description
 * @date 2022-03-08-下午 10:55
 *
 *
 * 单例模式思想  :
 *       构造器私有   保证内存中 只有 一个对象
 */
// 饿汉式 单例
public class _01Hungry {

    /**
     * 饿汉式 :
     *      浪费内存
     *      一上来就把内存 内存中的 所有 数据 全部加载进来 非常的耗内存
     */

    //可能会浪费空间
    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 _01Hungry () {

    }


    private final static _01Hungry HUNGRY = new _01Hungry ();

    public static _01Hungry getInstance() {
        return HUNGRY;
    }
}

懒汉式

1. 懒汉式 单线程 ok

//懒汉式
public class LazyMan1 {

    private LazyMan1() {

    }

    private static LazyMan1 lazyMan1;

    public static LazyMan1 getInstance() {
        if (lazyMan1 == null) {
            lazyMan1 = new LazyMan1();
        }
        return lazyMan1;
    }

    //单线程下 ok
    // 多线程 并发
}

2.懒汉式 多线程 no

//懒汉式
public class LazyMan2 {

    private LazyMan2() {
        System.out.println(Thread.currentThread().getName() + "ok" );
    }


    private static LazyMan2 lazyMan2;

    public static LazyMan2 getInstance() {
        if (lazyMan2 == null) {
            lazyMan2 = new LazyMan2();
        }
        return lazyMan2;
    }

    //多线线程 并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan2.getInstance();
            }).start();
        }
    }

}

//发现问题   需要加锁   加锁之前 还容易被两个线程 拿到  做两次检测

3.懒汉式   完整的双重检测锁DCL 加 原子性操作

加volatile,防止指令重排

//懒汉式   完整的双重检测锁 加 原子性操作
public class LazyMan3 {

    private LazyMan3() {
        System.out.println(Thread.currentThread().getName() + "ok");
    }


    private volatile static LazyMan3 lazyMan3;

    // 双重检测所模式 的 懒汉式 DCL 懒汉式
    public static LazyMan3 getInstance() {
        if (lazyMan3 == null) {
            synchronized (LazyMan3.class) {
                if (lazyMan3 == null) {
                    lazyMan3 = new LazyMan3(); //不是原子性操作
                    /**
                     * 1.  分配内存空间
                     * 2, 执行构造方法,初始化对象
                     * 3.  把这个对象指向这个空间
                     *
                     * 指令重排
                     *  123
                     *  132  A
                     *       B  此时LazyMan3 还没有完成构造
                     *       LazyMan3  避免指令重排 加 volatile
                     */
                }
            }
        }
        return lazyMan3;
    }

    //多线线程 并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMan3.getInstance();
            }).start();
        }
    }

}

4. 反射破坏单例  

//懒汉式   反射破坏单例
public class LazyMan4 {

    private LazyMan4() {
        System.out.println(Thread.currentThread().getName() + "ok");
    }


    private volatile static LazyMan4 lazyMan4;

    // 双重检测所模式 的 懒汉式 DCL 懒汉式
    public static LazyMan4 getInstance() {
        if (lazyMan4 == null) {
            synchronized (LazyMan4.class) {
                if (lazyMan4 == null) {
                    lazyMan4 = new LazyMan4(); //不是原子性操作
                    /**
                     * 1.  分配内存空间
                     * 2, 执行构造方法,初始化对象
                     * 3.  把这个对象指向这个空间
                     *
                     * 指令重排
                     *  123
                     *  132  A
                     *       B  此时LazyMan3 还没有完成构造
                     *       LazyMan3  避免指令重排 加 volatile
                     */
                }
            }
        }
        return lazyMan4;
    }

    //反射   反射可以破坏单例
    public static void main(String[] args) throws Exception {
        LazyMan4 instance1 = LazyMan4.getInstance();  //获得第一个对象
        Constructor<LazyMan4> declaredConstructor = LazyMan4.class.getDeclaredConstructor(null);  //空参构造器
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan4 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1.equals(instance2));  //false
        System.out.println(instance1);  //false
        System.out.println(instance2);  //false
    }

}

5.解决反射破坏单例   双重检测 升级  三级检测

//懒汉式   解决反射破坏单例   双重检测 升级  三级检测
public class LazyMan5 {

    private LazyMan5() {
        synchronized(LazyMan5.class) {
            if(lazyMan5 != null) {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }

    }


    private volatile static LazyMan5 lazyMan5;

    // 双重检测所模式 的 懒汉式 DCL 懒汉式
    public static LazyMan5 getInstance() {
        if (lazyMan5 == null) {
            synchronized (LazyMan5.class) {
                if (lazyMan5 == null) {
                    lazyMan5 = new LazyMan5(); //不是原子性操作
                }
            }
        }
        return lazyMan5;
    }

    //反射   反射可以破坏单例
    public static void main(String[] args) throws Exception {
        LazyMan5 instance1 = LazyMan5.getInstance();
        Constructor<LazyMan5> declaredConstructor = LazyMan5.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan5 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1.equals(instance2));  //异常
        System.out.println(instance1);
        System.out.println(instance2);
    }

}

6.用反射创建对象 破坏三重检测

但是仍然可以通过如下方式破坏:

    //反射   反射可以破坏单例
    public static void main(String[] args) throws Exception {
//        LazyMan6 instance1 = LazyMan6.getInstance();   常见创建这个对象
        Constructor<LazyMan6> declaredConstructor = LazyMan6.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan6 instance1 = declaredConstructor.newInstance();   // 反射 new newInstance
        LazyMan6 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1.equals(instance2));  //false
        System.out.println(instance1);
        System.out.println(instance2);
    }

}

7. 解决用反射创建 对象 秘钥

设置一个别人不知道的变量

//懒汉式   解决用反射创建 对象 秘钥
public class LazyMan7 {

    private static boolean zhangsan = false; //秘钥

    private LazyMan7() {
        synchronized(LazyMan7.class) {
            if (zhangsan == false) {
                zhangsan = true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");

            }
        }

    }


    private volatile static LazyMan7 lazyMan7;

    // 双重检测所模式 的 懒汉式 DCL 懒汉式
    public static LazyMan7 getInstance() {
        if (lazyMan7 == null) {
            synchronized (LazyMan7.class) {
                if (lazyMan7 == null) {
                    lazyMan7 = new LazyMan7(); //不是原子性操作
                }
            }
        }
        return lazyMan7;
    }

    //反射   反射可以破坏单例
    public static void main(String[] args) throws Exception {
//        LazyMan6 instance1 = LazyMan6.getInstance();   常见创建这个对象
        Constructor<LazyMan7> declaredConstructor = LazyMan7.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan7 instance1 = declaredConstructor.newInstance();   // 反射 new newInstance
        LazyMan7 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1.equals(instance2));  //异常
        System.out.println(instance1);
        System.out.println(instance2);
    }

}

8.破坏  两个反射创建 对象 秘钥

//懒汉式   破坏  两个反射创建 对象 秘钥
public class LazyMan8 {

    private static boolean zhangsan = false; //秘钥

    private LazyMan8() {
        synchronized(LazyMan8.class) {
            if (zhangsan == false) {
                zhangsan = true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");

            }
        }

    }


    private volatile static LazyMan8 lazyMan8;

    // 双重检测所模式 的 懒汉式 DCL 懒汉式
    public static LazyMan8 getInstance() {
        if (lazyMan8 == null) {
            synchronized (LazyMan8.class) {
                if (lazyMan8 == null) {
                    lazyMan8 = new LazyMan8(); //不是原子性操作
                }
            }
        }
        return lazyMan8;
    }

    //反射   反射可以破坏单例
    public static void main(String[] args) throws Exception {
//        LazyMan6 instance1 = LazyMan6.getInstance();   常见创建这个对象

        Field zhangsan = LazyMan8.class.getDeclaredField("zhangsan");  //获取字段
        zhangsan.setAccessible(true);

        Constructor<LazyMan8> declaredConstructor = LazyMan8.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//无视私有构造器
        LazyMan8 instance1 = declaredConstructor.newInstance();   // 反射 new newInstance

        zhangsan.set(instance1, false);

        LazyMan8 instance2 = declaredConstructor.newInstance();

        System.out.println(instance1.equals(instance2));  //false
        System.out.println(instance1);
        System.out.println(instance2);
    }

}

单例不安全,使用枚举 

枚举自带单例模式

public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }


}

test

class Test{

    public static void main(String[] args) {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance1.equals(instance2));  //true
        System.out.println(instance1);                    //INSTANCE
        System.out.println(instance2);                    //INSTANCE

    }
}

 

 破坏枚举,发现问题

class Test{

    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();


        System.out.println(instance1.equals(instance2));  //true
        System.out.println(instance1);                    //INSTANCE
        System.out.println(instance2);                    //INSTANCE

    }
}

 正常应该报错 

Cannot reflectively create enum objects 不能反射地创建enum对象

idea欺骗了我们

class Test{

    public static void main(String[] args) throws Exception {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        //NoSuchMethodException: com.gh.EnumSingle.<init>()  异常 类里面没有空参的构造器
        System.out.println(instance1.equals(instance2));  //true
        System.out.println(instance1);                    //INSTANCE
        System.out.println(instance2);                    //INSTANCE

    }
}

使用jad工具反编译为java

枚举类型的最终反编译源码:

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
public final class EnumSingle extends Enum
{
    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }
    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/kuang/single/EnumSingle, name);
    }
    private EnumSingle(String s, int i)
    {
        super(s, i);
    }
    public EnumSingle getInstance()
    {
        return INSTANCE;
    }
    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];
    static
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
                INSTANCE
        });
    }
}

 源码骗了我们,用了一个有参构造器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gh-xiaohe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值