设计模式-单例模式

1、定义

单例模式(Singleton Pattern)保证某个类在运行期间,只有一个实例对外提供服务,而这个类被称为单例类。

2、结构图

在这里插入图片描述

3、单例的实现

3.1、饿汉式

/**
 *单例模式-饿汉式
 *      在类加载期间初始化私有的静态实例,保证instance实例创建过程是线程安全的.
 *      特点: 不支持延时加载,获取实例对象的速度比较快,但是如果对象比较大,而且一直没有使用就会造成内存的浪费.
 * @author zhy
 */
public class Singleton_01 {

    //1. 私有构造方法
    private Singleton_01(){

    }

    //2. 在本类中创建私有静态的全局对象
    private static final Singleton_01 INSTANCE = new Singleton_01();


    //3. 提供一个全局访问点,供外部获取单例对象
    public static  Singleton_01 getInstance(){

        return INSTANCE;
    }
}

3.2、懒汉式

/**
 * 懒加载
 * 单例模式-双重校验
 * @author zhy
 */
public class Singleton_02 {

    //1. 私有构造方法
    private Singleton_02(){

    }

    //2. 在本类中创建私有静态的全局对象
    // 使用 volatile保证变量可见性,屏蔽指令重排序
    private volatile static Singleton_02 instance;

    //3. 获取单例对象的静态方法
    public static  Singleton_02 getInstance(){

        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
        if(instance == null){
            synchronized (Singleton_02.class){
                //第二次判断,抢到锁之后再次进行判断,判断是否为null
                if(instance == null){

                    instance = new Singleton_02();
                    /*
                      上面的创建对象的代码,在JVM中被分为三步:
                           1.分配内存空间
                           2.初始化对象
                           3.将instance指向分配好的内存空间
                     */

                }
            }
        }

        return instance;
    }

3.3、静态内部类

/**
 * 单例模式 -静态内部类  
 * @author zhy
 * @date 2022/12/3 14:36
 */
public class Singleton_03 {

    private Singleton_03(){

        if(SingletonHandler.instance != null){
            //throw new RuntimeException("不允许非法访问");
        }
    }

    //创建静态内部类
    private static class SingletonHandler{

        //在静态内部类中创建单例,在装载内部类的时候,才会创建单例对象
        private static Singleton_03 instance = new Singleton_03();
    }


    public static Singleton_03 getInstance(){

        return  SingletonHandler.instance;
    }
}

3.4、枚举

/**
 * 单例模式-枚举
 *       阻止反射的破坏: 在反射方法中不允许使用反射创建枚举对象
 *       阻止序列化的破坏: 在序列化的时候仅仅是将枚举对象的name属性输出到了结果中,反序列化的时候,就会通过
 *       Enum的 valueOf方法 来根据名字去查找对应枚举对象.
 * @author zhy
 */

public enum Singleton_04 {

    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static Singleton_04 getInstance(){

        return INSTANCE;
    }
}

3.5、测试

//反射对单列的破坏
    @Test
    void reflect() throws Exception {
        Class<Singleton_03> clazz = Singleton_03.class;

        Constructor<Singleton_03> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true); //设置为true后,就可以对类中的私有成员进行操作

        Singleton_03 instance1 = constructor.newInstance();

        Singleton_03 instance = constructor.newInstance();
        System.out.println(instance == instance1);
    }

    //反序列化对单列的破坏
    @Test
    void test() throws Exception {

        //序列化对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile.obj"));
        oos.writeObject(Singleton_04.getInstance());


        //序列化对象输入流
        File file = new File("tempFile.obj");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Singleton_04 singleton03 = (Singleton_04) ois.readObject();


        System.out.println(singleton03);
        System.out.println(Singleton_04.getInstance());

        System.out.println(Singleton_04.getInstance() == singleton03);//false
    }


    @Test
    void ser() throws Exception {

        //序列化对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile.obj"));
        oos.writeObject(Singleton.getInstance());


        //序列化对象输入流
        File file = new File("tempFile.obj");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Singleton singleton03 = (Singleton) ois.readObject();


        System.out.println(singleton03);
        System.out.println(Singleton.getInstance());

        System.out.println(Singleton.getInstance() == singleton03);//false
    }
}
    /**
     * 单例类实现序列化接口
     */
    class Singleton implements Serializable {

        private volatile static Singleton singleton;

        private Singleton() {

        }

        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }

        /**
         * 只需要在单例类中定义readResolve方法,就可以解决序列化对于单例的破坏
         * 程序会判断是否有readResolve方法,如果有就执行该方法,如果不存在---就会创建一个新的对象
         */
        private Object readResolve(){

            return singleton;
        }
    }

4、总结

饿汉式

  • 饿汉式的实现方式,在类加载的期间,就已经将 instance 静态实例初始化好了,所以,instance 实例的创建是线程安全的。不过,这样的实现方式不支持延迟加载实例。

懒汉式-双重检测

  • 双重检测实现方式既支持延迟加载、又支持高并发的单例实现方式。只要 instance 被创建之后,再调用 getInstance() 函数都不会进入到加锁逻辑中。所以,这种实现方式解决了懒汉式并发度低的问题。

静态内部类

  • 利用 Java 的静态内部类来实现单例。这种实现方式,既支持延迟加载,也支持高并发,实现起来也比双重检测简单。

枚举方式

  • 最简单的实现方式,基于枚举类型的单例实现。这种实现方式通过 Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性(同时阻止了反射和序列化对单例的破坏)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值