14.单例模,枚举

单例模式

饿汉式
/*
 * 饿汉式单例
 * 如果这个实例从未被使用,会造成内存浪费
 * */
public class HungryMan {
    private final static HungryMan hungryMan = new HungryMan();

    private HungryMan() {
        //System.out.println(Thread.currentThread().getName()); 只会执行一次
    }

    public static HungryMan getInstance() {
        return hungryMan;
    }
}

class Test1 {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                HungryMan instance = HungryMan.getInstance();
                //System.out.println(instance);//每个对象都相等
            }).start();
        }
        /*HungryMan instance1 = HungryMan.getInstance();
        HungryMan instance2 = HungryMan.getInstance();
        System.out.println(instance1 == instance2);*/
    }
}
懒汉式

双重检测锁DCL,反射不安全

/*
 * 双重检测锁 懒汉式 DCL
 * volatile 避免指令重排
 * 双重保证多线程下对象只有一个
 * */
public class LazyMan {
    private volatile static LazyMan lazyMan;

    private LazyMan() {
        System.out.println(Thread.currentThread().getName());
    }

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是原子操作
                    /*
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     * 期待的是123
                     * A线程由于指令重排变成了 132
                     * B线程 进来,发现lazyMan!=null 就直接返回了,就有问题了,此时lazyMan还没有完成构造
                     * 结局:volatile避免指令重排
                     * */
                }
            }
        }
        return lazyMan;
    }
}

class Test2 {
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                LazyMan instance = LazyMan.getInstance();
                //System.out.println(instance);
            }).start();
        }
    }
}

静态内部类

//静态内部类 懒汉式
public class Holder {
    private Holder() {

    }

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

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }
}

升级版

/*
 * 双重检测锁 懒汉式 DCL
 * */
public class LazyMan {
    private volatile static LazyMan lazyMan;
    private static boolean flag = false;

    //构造方法中也加锁
    private LazyMan() {
        synchronized (LazyMan.class) {
            if (!flag) {
                flag = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏,异常");
            }
            /*if (lazyMan != null) {
                throw new RuntimeException("不要试图使用反射破坏,异常");
            }*/
        }
        System.out.println(Thread.currentThread().getName());
    }

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是原子操作
                    /*
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     * 期待的是123
                     * A线程由于指令重排变成了 132
                     * B线程 进来,发现lazyMan!=null 就直接返回了,就有问题了,此时lazyMan还没有完成构造
                     * 结局:volatile避免指令重排
                     * */
                }
            }
        }
        return lazyMan;
    }
}

class Test2 {
    public static void main(String[] args) throws Exception {
        /*for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                LazyMan instance = LazyMan.getInstance();
                //System.out.println(instance);
            }).start();
        }*/

        //反射破解DCL
        //解决:构造方法中也加锁
       /* LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        LazyMan instance2 = constructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);//instance1和instance2不是一个对象*/

        //再次破解,只通过反射创建对象,结果又成功了
        //解决:信号灯
        /*Constructor<LazyMan> constructor1 = LazyMan.class.getDeclaredConstructor(null);
        constructor1.setAccessible(true);
        LazyMan instance3 = constructor1.newInstance();
        LazyMan instance4 = constructor1.newInstance();
        System.out.println(instance3);
        System.out.println(instance4);*/

        //再次破击,前提知道了你的信号灯是谁
        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);

        Constructor<LazyMan> constructor5 = LazyMan.class.getDeclaredConstructor(null);
        constructor5.setAccessible(true);
        LazyMan instance5 = constructor5.newInstance();
        flag.set(instance5, false);//重新设置信号灯,
        LazyMan instance6 = constructor5.newInstance();
        System.out.println(instance5);
        System.out.println(instance6);
    }
}

枚举 自带单例

public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

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

        //1.
        //查看EnumSingle的class文件,发现有一个无参构造
        //用反射去破解,但是异常了,根本没有无参构造,
        // 用javap命令发现也是有无参构造的,
        // 再用jad.exe把源码变成java文件,发现根本不是无参构造,而是有参 EnumSingle(String s,int i){}
       /* Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        EnumSingle instance2 = constructor.newInstance();
        System.out.println(instance2);//NoSuchMethodException*/

        //2.再次测试,成功了
        Constructor<EnumSingle> constructor1 = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        constructor1.setAccessible(true);
        EnumSingle instance3 = constructor1.newInstance();
        System.out.println(instance1);
        System.out.println(instance3);//IllegalArgumentException: Cannot reflectively create enum objects
    }
}
posted @   jpy  阅读(3)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示