单例模式

参考

单例设计模式

在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。

使用单例模式的好处:

  • 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。

饿汉模式

// 饿汉式单例
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 final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }

}

懒汉式单例

package com.example.springboot_threadlocal.single;

public class LazyMan {

    private static boolean flag = false;

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

        }

//        System.out.println(Thread.currentThread().getName() + "\tOK");
    }

    // 加上volatile避免指令重排
    private volatile static LazyMan lazyMan;

    //双重检测锁模式,懒汉式单例 DCL懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (null == lazyMan) {
                    lazyMan = new LazyMan();
                    /**
                     * 创建对象的三个步骤
                     * 1. 分配内存空间
                     * 2. 执行构造方法,初始化对象
                     * 3. 把这个对象指向这个空间
                     *
                     * A 线程 1 3 2
                     * B线程进来发现已经A线程中,对象已经指向了内存空间,就认为这个lazyMan不为空,直接返回,但是实际上
                     * 这个lazyMan没有初始化对象,会出问题
                     */
                }
            }
        }
        return lazyMan;
    }

}

静态内部类

package com.example.springboot_threadlocal.single;

public class Holder {

    // 单例模式必须构造器私有
    private Holder(){

    }

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

    // 静态内部类方式
    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

单例不安全,因为有反射

可以使用反射破坏单例

 @Test
    void usingClass2() throws Exception {
        // 使用flag进行限制,对flag进行反射破坏
        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);

        // 使用反射创建对象可能会使单例模式对象遭到破坏
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);

        declaredConstructor.setAccessible(true);
        // 都使用反射破坏
        LazyMan instance1 = declaredConstructor.newInstance();

        // 把instance中的flag设置为false
        flag.set(instance1, false);

        LazyMan instance2 = declaredConstructor.newInstance();
        // 在构造方法里面没有加锁的话,这两个是会不一样的
        System.out.println(instance1 == instance2);

    }

枚举

枚举里面没有无参构造,只有有参构造,不能通过反射破坏单例

jad反编译枚举
jad -sjava EnumSingle.class

// 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

package com.example.springboot_threadlocal.single;


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/example/springboot_threadlocal/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
        });
    }
}

尝试使用反射破坏枚举

@Test
    void test1() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 使用反编译破坏枚举单例
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);

        declaredConstructor.setAccessible(true);

        EnumSingle enumSingle1 = declaredConstructor.newInstance();
        EnumSingle enumSingle2 = declaredConstructor.newInstance();

        System.out.println(enumSingle1);
        System.out.println(enumSingle2);

    }

显示不能使用反射创建枚举类对象

java.lang.IllegalArgumentException: Cannot reflectively create enum objects

	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.example.springboot_threadlocal.single.EnumSingleTest.test1(EnumSingleTest.java:24)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
posted @   Kelvin's  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示