首页 > 其他分享 >单例模式

单例模式

时间:2024-06-06 12:01:35浏览次数:20  
标签:HungrySingleton getInstance 模式 instance static private 单例 class

DESC

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

场景:

重量级的对象,不需要多个实例,如线程池,数据库连接池

实现

1. 懒汉模式

  • 延迟加载的方式 只有在真正使用的时候,才开始实例化
  • 线程安全问题
  • double check 加锁优化
  • 编译器(JIT) cpu有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字,对于volatile修饰的字段,可以防止指令重排
class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){}
    public static LazySingleton getInstance(){
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();// 1.分配空间 2.初始化 3.引用赋值
                }
            }
        }
        return instance;
    }
}

备注:
javap -v XXX.class可以看class文件的字节码

2. 饿汉模式

  • 类加载的初始化阶段就完成了实例的初始化,本质上是基于JVM类加载机制,保证实例的唯一性
  • 类加载的过程:
    • 加载二进制数据到内存中,生成对应的class数据结构
    • 连接:验证、准备(给类的静态成员变量赋默认值)、解析
    • 初始化:给类的静态变量赋值
      注意:
    • 只有在真正使用对应的类时,才会触发初始化
class HungrySingleton{
    private static final long serialVersionUID = 4416608876659526091L;
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return instance;
    }
}

3. 静态内部类

  • 本质上是利用类的加载机制保证线程安全
  • 只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式
class InnerClassSingleton{
    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){}
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }
}

4. 反射攻击实例

public class HungrySingletonTest {
    public static void main(String[] args) throws Exception {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance);
        System.out.println(instance1);

//        // 反射获取实例
        Constructor<HungrySingleton> declaredConstructor = HungrySingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        HungrySingleton instance2 = declaredConstructor.newInstance();
        System.out.println(instance2);  
    } 
}

class HungrySingleton{ 
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){
        if (instance != null) {
            throw new RuntimeException("单例不允许创建多个实例!");
        }
    }
    public static HungrySingleton getInstance(){
        return instance;
    } 
}

5. 枚举

枚举类型支持反序列化的操作 并且不能用反射攻击
其他类型支持反序列化操作案例

public class HungrySingletonTest {
    public static void main(String[] args) throws Exception {
        HungrySingleton instance = HungrySingleton.getInstance();
        HungrySingleton instance1 = HungrySingleton.getInstance();
        System.out.println(instance);
        System.out.println(instance1);

//        // 反射获取实例
//        Constructor<HungrySingleton> declaredConstructor = HungrySingleton.class.getDeclaredConstructor();
//        declaredConstructor.setAccessible(true);
//        HungrySingleton instance2 = declaredConstructor.newInstance();
//        System.out.println(instance2);

        // 序列化
        HungrySingleton instance2 = HungrySingleton.getInstance();
//        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
//        oss.writeObject(instance2);
//        oss.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));
        HungrySingleton o = (HungrySingleton) ois.readObject();
        ois.close();
        System.out.println(o == instance2);

    }

}

class HungrySingleton implements Serializable{
    private static final long serialVersionUID = 4416608876659526091L;
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){
        if (instance != null) {
            throw new RuntimeException("单例不允许创建多个实例!");
        }
    }
    public static HungrySingleton getInstance(){
        return instance;
    }

    public Object readResolve() throws ObjectStreamException {
        return getInstance();
    }
}

标签:HungrySingleton,getInstance,模式,instance,static,private,单例,class
From: https://www.cnblogs.com/zrx1/p/18234874

相关文章

  • react中推荐使用发布订阅模式,进行跨多层级的组件间通信和事件传递吗?
    在React中,虽然发布订阅模式(Pub/Sub)可以作为一种实现跨多层级组件间通信的方法,但它并不是React官方推荐的主要手段,尤其是在ReactHooks和ContextAPI普及之后。React推荐的跨组件通信方法主要包括:Propsdrilling:最直接的方式,通过props从父组件向子组件传递数据,适合简单的数据流......
  • 跟着GPT学设计模式之观察者模式
    你好,这里是codetrend专栏“跟着GPT学设计模式”。引言观察者模式(ObserverPattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。观察者模式(ObserverDesignPattern)也被称为发布订阅模式(Publish-S......
  • 【设计模式】观察者模式
    设计模式【设计模式】工厂方法模式【设计模式】抽象工厂模式【设计模式】单例模式【设计模式】策略模式【设计模式】观察者模式 一、介绍观察者模式是一种行为设计模式,当一个对象的状态发生改变时,依赖(观察)它的对象会接收到通知,并进行自动的更新操作。举例:某公司发布了......
  • Vue 3 Composition API与Hooks模式
    Vue3的CompositionAPI引入了Hook函数的概念,这是一种更加模块化和可重用的状态管理和逻辑组织方式。自定义Hook首先,我们创建一个自定义Hook,例如useCounter,它封装了计数器的逻辑://useCounter.jsimport{ref}from'vue';exportfunctionuseCounter(){c......
  • SAP: SALV 利用控制器的模式
    SAP:SALV 例子利用控制器的模式利用控制器的SALV方法与实际业务中最常用的利用GRID显示ALV的方法其步骤一样。为了在画面上显示ALV事例,必须要存在连接画面和ALVGRID控件的SAP容器控件。SAP控制器为了充当LINKER将SAP控件包含于自己的领域。SAP控件包含SAPTree、SAPPIC......
  • 存储引擎及特点、约束条件、严格模式、基本字段类型(整型、浮点型、字符串、日期时间
    【一】存储引擎在平常我们处理的文件格式有很多,并且针对不同的文件格式会有对应不同的存储方式和处理机制针对不同的数据应该有对应不同的处理机制存储引擎就是不同的处理机制。#查看所有引擎showengines;四种主要的存储引擎(1)Innodb引擎是MySQL5.5版本之后的默认存......
  • 【设计模式】工厂模式(创建型)⭐⭐⭐
    文章目录1.概念1.1什么是工厂模式1.2优点与缺点2.实现方式2.1简单工厂模式(SimpleFactory)2.2简单工厂模式缺点2.3抽象工厂模式(AbstractFactoryPattern)3.Java哪些地方用到了工厂模式4.Spring哪些地方用到了工厂模式1.概念1.1什么是工厂模式工厂模式属......
  • python执行模式
    Python执行模式目录Python执行模式命令行模式交互模式好处:坏处:命令行模式命令行模式:写好命令之后,保存并运行整个文件。运行的时候,python解释器会一行一行对文件进行解析和执行。交互模式交互模式:输入一行后,python立即执行,并展示运行结果。好处:不需要创建任何新文件,py......
  • 基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道
    作者:尹航在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流量泳道的概念、使用流量泳道进行全链路灰度管理的方案,以及阿里云服务网格ASM提供的严格模式与宽松模式的......
  • 正则表达式学习(1)——模式
    正则表达式用于处理字符和字符串,是一种强大的工具1.正则表达式的模式字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。特殊字符:例如点号.、星号*、加号+、问号?等,它们具有特殊的含义和功能。字符类:用方括号[]包围的字符集合,用于匹配方括号内的任......