为什么单例模式有各种写法,枚举写单例有什么好?聊一聊我的理解

本文详细总结了Java中的单例模式,包括饿汉模式的普通实现、枚举实现和静态块实现,以及懒汉模式的普通实现和线程安全的DCL(双重检查锁定)实现。枚举实现因其线程安全和防止反射攻击的特点被推荐使用。同时,静态内部类实现也是一种高效且安全的单例模式。文章还探讨了单例模式在节省系统资源和控制实例数量上的应用,并分析了各种实现方式的优缺点。
摘要由CSDN通过智能技术生成

单例模式:

在学习了狂神和尚硅谷燕姐的视频之后,写一个总结!
  • 单例模式:①构造器私有;②自己创建唯一且私有实例;③获取实例的方法公有;

  • 解决的问题:一个全局使用的类频繁地创建与销毁。

  • 什么时候使用:想控制实例数目,节省系统资源的时候。

  • 存在的问题:重点分析懒汉模式,因为饿汉模式是线程安全的,没有花里胡哨的问题;而且有点浪费空间,不管用不用,对象都已经被创建;

使用枚举实现(解决反射)
枚举是抽象类,无法直接实例;只能用实现类里面提供的对象,而且属性用final修饰,在类加载的时候初始化,所以是线程安全的,而且反射都不好使。
因为反射的原理是通过Constructor类的newInstance()方法创建对象的,里面有判断如果是枚举类型,则禁止创建;

饿汉模式:先创建实例,需要用的时候直接用
懒汉模式:需要使用的时候再创建出来
在这里插入图片描述

  • 饿汉模式的三种实现:
    1、普通实现,见代码;
    2、枚举实现,减代码
    3、静态块实现,见注释,可以再实例化之前读取一些配置文件的参数来创建实例,适用于复杂场景;
public class Singleton01 {

    //1、普通 饿汉
    //public static final Singleton01 INSTANCE = new Singleton01();

    //3、静态代码块 饿汉,可以通过静态块来实现复杂场景,比如,需要通过配置文件读取参数等
    public static final Singleton01 INSTANCE;

    static{
        INSTANCE = new Singleton01();
    }

	//1、3都可以用的部分
    private Singleton01(){//构造私有

    }
}
/**
 * 饿汉模式
 * 2、枚举实现
 */
public enum Singleton02 {//枚举实现饿汉
    INSTANCE;
}

懒汉模式的三种方式:
1、普通实现
2、线程安全的DCL实现
3、静态内部类实现

/**
* 问题:当多线程同时判断通过,进入if(INSTANCE == null)
* 就会导致被创建出多个实例对象出来
*/
public class SingleLazy {
    private static SingleLazy INSTANCE;

    private SingleLazy(){

    }
    public static SingleLazy getINSTANCE(){
        if(INSTANCE == null){
            INSTANCE = new SingleLazy();
        }
        return INSTANCE;
    }
}

DCL实现单例:双层检测+synchronized
下面代码的问题:发生指令重排时,按照132顺序执行(请看注释),导致B线程执行到外层if时,认为空间有对象,去直接返回对象,结果对象还没有被实例完全

解决方案:
private static SingleLazy INSTANCE;
替换为**(加volatile)**
private volatile static SingleLazy INSTANCE;
禁止指令重排;

/**
* 问题:INSTANCE = new SingleLazy();创建过程分为三步
* 1、开辟空间
* 2、实例对象
* 3、对象指向内存空间
*/
public class SingleLazy {
    private static SingleLazy INSTANCE;

    private SingleLazy(){

    }

    public static SingleLazy getINSTANCE(){
        if(INSTANCE == null){//解决效率问题,如果已经有对象了就不用去等锁了
            synchronized(SingleLazy.class){//解决多线程问题,确保单例,都卡在if里面的话,可能创建多个对象出来
                if(INSTANCE == null){//锁完还是空就放心创建
                    INSTANCE = new SingleLazy();
                }
            }
        }
        return INSTANCE;
    }
}

内部类实现
通过内部类来实现,可以保证安全。
因为只有在内部类,被(提供的公开方法)调用的时候才会被类加载器加载,同时创建单例对象,类加载器安全,所以单例安全

public class SingleLazy2 {
    private static class Inner{
        private static final SingleLazy2 INSTANCE = new SingleLazy2();
    }
    private SingleLazy2(){

    }
    public static SingleLazy2 getInstance(){
        return Inner.INSTANCE;
    }
}

结论:

使用枚举实现(解决反射)
因为反射的原理是通过Constructor类的newInstance()方法创建对象的,里面有判断如果是枚举类型,则禁止创建;

内部类实现
类加载器安全,所以单例安全。

单例参考:
狂神讲解单例
尚硅谷燕姐讲解单例

理解枚举,更好的理解单例:
枚举的底层原理

Java之枚举

Java枚举详解七种常见用法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值