头一次见单例模式讲的如此透彻

一次,模式,如此,透彻 · 浏览次数 : 599

小编点评

**单例模式简介** 单例模式是一种常用的软件设计模式,用于创建类型。通过单例模式的方法创建的类在当前进程中只有一个实例。 **单例模式的优点** * 提供对唯一实例的受控访问 * 减少了内存开销 * 避免了对资源的多重占用 **单例模式的缺点** *不支持继承 * 违反了单一职责原则 * 不支持有参数的构造函数 * 可能存在反射或反序列化攻击 **Java 中的单例模式实现** 五种不同的单例实现方法: * 饿汉式 * 懒汉式 * 双重检测 * 静态内部类 *枚举类 **饿汉式** 饿汉式是一种简单易用的单例模式实现方式。在类加载的时候,就创建并初始化一个静态的实例对象,然后通过一个静态方法返回这个实例。 **懒汉式** 懒汉式是一种支持延迟加载的单例模式实现方式。在第一次调用获取实例的方法时,才创建并初始化一个静态的实例对象,然后返回这个实例。为了保证线程安全性,需要给获取实例的方法加上 `synchronized`关键字。 **双重检测** 双重检测是一种支持延迟加载的单例模式实现方式。在第一次调用获取实例的方法时,先判断静态的实例对象是否为空,如果为空,则进入同步代码块,再判断一次是否为空,如果为空,则创建并初始化一个静态的实例对象,然后返回这个实例。 **静态内部类** 静态内部类是一种利用了 Java 静态内部类的特性,即外部类加载时不会加载内部类,只有在使用到内部类时才会加载。 **枚举类** 枚举类是一种提供线程安全性和唯一性的单例模式实现方式。枚举类在加载时会创建所有枚举常量,并且保证了线程安全性和唯一性。

正文

简介

单例模式是一种常用的软件设计模式,用于创建类型。通过单例模式的方法创建的类在当前进程中只有一个实例。单例模式的类只能允许一个实例存在。单例模式的作用是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。

组成部分:

  1. 私有化构造方法。
  2. 私有化内部实例。
  3. 公有静态方法用来获取内部实例。

单例模式

优缺点

单例模式的优点有:

  • 提供了对唯一实例的受控访问,可以保证对象的唯一性和一致性。
  • 减少了内存开销,避免了频繁的创建和销毁对象。
  • 避免了对资源的多重占用,例如文件操作、数据库连接等。

单例模式的缺点有:

  • 不支持继承和多态,违反了单一职责原则,一个类应该只关心内部逻辑,而不关心外部如何实例化。
  • 不易扩展,如果需要创建多个实例,就需要修改代码,违反了开闭原则,一个类应该对扩展开放,对修改关闭。
  • 不支持有参数的构造函数,如果需要传递参数,就需要修改方法或者定义其他方法。
  • 可能存在反射或者反序列化攻击,破坏单例的唯一性。

应用场景

单例模式适用于以下场景:

  • 需要频繁创建和销毁的对象,例如缓存、线程池、注册表等。
  • 需要控制资源的访问,例如文件操作、数据库连接等。
  • 需要保证对象的唯一性和一致性,例如配置信息、全局变量等。

Java 代码示例

在 Java 中,有五种不同的单例实现方法。其中包括饿汉式、懒汉式、双检锁、静态内部类和枚举类。
单例模式的五种实现原理分别是饿汉式、懒汉式、双重检测、静态内部类和枚举类。它们各自的优缺点如下:

  • 饿汉式:原理是在类加载的时候,就创建并初始化一个静态的实例对象,然后通过一个静态的方法返回这个实例。优点是线程安全,不需要加锁;缺点是不支持延迟加载,可能会浪费资源。
public class Singleton {
    private Singleton() {}
    private static Singleton instance;
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 懒汉式:原理是在第一次调用获取实例的方法时,才创建并初始化一个静态的实例对象,然后返回这个实例。为了保证线程安全,需要给获取实例的方法加上synchronized关键字。优点是支持延迟加载,节省资源;缺点是线程不安全,需要加锁,影响性能。
public class Singleton {
    private Singleton() {}
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}
  • 双重检测:原理是在第一次调用获取实例的方法时,先判断静态的实例对象是否为空,如果为空,则进入同步代码块,再判断一次是否为空,如果为空,则创建并初始化一个静态的实例对象,然后返回这个实例。为了防止指令重排序导致空指针异常,需要给静态的实例对象加上volatile关键字。优点是线程安全,支持延迟加载,不需要加锁;缺点是可能会出现空指针异常,需要使用 volatile 关键字防止指令重排序。
public class Singleton {
    private Singleton() {}
    private static volatile Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 静态内部类:原理是利用了 Java 静态内部类的特性,即外部类加载时不会加载内部类,只有在使用到内部类时才会加载。因此,在第一次调用获取实例的方法时,才会加载静态内部类,并创建并初始化一个静态的实例对象,然后返回这个实例。优点是线程安全,支持延迟加载,不需要加锁;缺点是不能防止反射或者反序列化攻击。
public class Singleton {
    private Singleton() {}
    private static class Instance {
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return Instance.instance;
    }
}
  • 枚举类:原理是利用了Java枚举类型本身的特性,即枚举类型在加载时就会创建所有的枚举常量,并且保证了线程安全性和唯一性。因此,在调用获取实例的方法时,直接返回枚举常量即可。优点是线程安全,简单易用,可以防止反射或者反序列化攻击;缺点是不支持延迟加载,不能继承其他类。
public enum Singleton {
     INSTANCE;
}

这些不同的实现方式有不同的适用场景,需要根据具体的需求和条件来选择。在这里,我只能给出一些个人的看法,仅供参考。

  • 如果对内存资源比较敏感,或者单例对象不需要频繁使用,可以考虑使用懒汉式或者双重检测,因为它们支持延迟加载,可以节省资源。
  • 如果对性能比较敏感,或者单例对象需要频繁使用,可以考虑使用饿汉式或者静态内部类,因为它们不需要加锁,可以提高效率。
  • 如果对安全性比较敏感,或者需要防止反射或者反序列化攻击,可以考虑使用枚举类,因为它可以保证实例的唯一性和不可变性。
  • 如果对简洁性比较敏感,或者不需要继承其他类,可以考虑使用枚举类,因为它是最简单的实现方式。

个人来说在编码效率和可维护性上我比较倾向于使用静态内部类的实现方式,既能保证线程安全性,又能支持延迟加载。

Spring 代码示例

在 Spring 框架中,Spring 默认使用单例模式来创建和管理 Bean 对象,但是可以通过 @Scope("singleton") 注解来指定 Bean 对象的作用域。

  • @Scope("singleton"):表示该Bean对象是一个单例对象,在整个Spring容器中只有一个实例。
  • @Scope("prototype"):表示该Bean对象是一个原型对象,在每次请求时都会创建一个新的实例。
  • @Scope("request"):表示该Bean对象的作用域是一个HTTP请求,在同一个请求中只有一个实例。
  • @Scope("session"):表示该Bean对象的作用域是一个HTTP会话,在同一个会话中只有一个实例。

总结

单例模式是一种简单而常用的设计模式,它可以保证一个类只有一个实例,并提供一个全局访问点。单例模式有多种实现方式,各有优缺点。单例模式可以节约系统资源,避免资源冲突,保证对象的唯一性和一致性。但是单例模式也有不利于继承和扩展的缺点,以及可能存在的安全隐患。在使用单例模式时,需要根据具体情况和需求选择合适的方法,并注意避免潜在的问题。

关注公众号【waynblog】每周分享技术干货、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!

与头一次见单例模式讲的如此透彻相似的内容:

头一次见单例模式讲的如此透彻

## 简介 单例模式是一种常用的软件设计模式,用于创建类型。通过单例模式的方法创建的类在当前进程中只有一个实例。单例模式的类只能允许一个实例存在。单例模式的作用是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。 组成部分: 1. 私有化构造方法。 2. 私有化内部实例。 3.

记一次 .NET 某安全生产信息系统 CPU爆高分析

一:背景 1.讲故事 今天是🐏的第四天,头终于不巨疼了,写文章已经没什么问题,赶紧爬起来写。 这个月初有位朋友找到我,说他的程序出现了CPU爆高,让我帮忙看下怎么回事,简单分析了下有两点比较有意思。 这是一个安全生产的信息管理平台,第一次听说,我的格局小了。 这是一个经典的 CPU 爆高问题,过往

[转帖]JVM CPU过高排查之路

https://www.jianshu.com/p/97860bbeb45c 双十一了,头一天晚上10点左右收到阿里云cpu超过90%短信报警。 第二天上班了,开始处理,步骤如下: 1、top找出cpu高的java进程号9592 2、top -Hp 9592查看cpu占用time最高的线程编号281

文心一言 VS 讯飞星火 VS chatgpt (112)-- 算法导论10.2 3题

三、用go语言,用一个单链表L实现一个队列。要求操作 ENQUEUE 和 DEQUEUE 的运行时间仍为O(1)。 文心一言: 在 Go 语言中,使用单链表实现队列的操作,包括入队(ENQUEUE)和出队(DEQUEUE),并保持操作的时间复杂度为 O(1),需要利用两个指针,一个指向队头,另一个指

谁说爬虫只能Python?看我用C#快速简单实现爬虫开发和演示!

前言:说到爬虫,基本上清一色的都知道用Python,但是对于一些没玩过或者不想玩Python的来说,却比较头大一点。所以以下我站在C# 的角度,来写一个简单的Demo,用来演示C# 实现的简单小爬虫。大家感兴趣可以自己拓展出更加丰富的爬虫功能。 前提:引用包HtmlAgilityPack 先来个爬取

案例分享-丢失的请求头

拍摄于富平中华郡 背景 今天组内一个小哥找我协助看一个问题,现象是他开放了一个Api给第三方调用,需要在http中传递一个名字为access_token的头,但是发布到测试环境以后却怎么也获取不到这个头,本地调试是没有问题的,希望协助看看。 排查 http传递头还会出问题,这都是很成熟的东西了,大概

Kafka源码分析(四) - Server端-请求处理框架

系列文章目录 https://zhuanlan.zhihu.com/p/367683572 一. 总体结构 先给一张概览图: 服务端请求处理过程涉及到两个模块:kafka.network和kafka.server。 1.1 kafka.network 该包是kafka底层模块,提供了服务端NIO通信

[转帖]HTTP 安全响应头(Security Response header)配置手册

https://sysin.org/blog/security-headers/ 一、常用安全 Header 释义 1. Strict-Transport-Security (HSTS) HTTP Strict Transport Security(通常简称为 HSTS)是一个安全功能,它告诉浏览器

【AppStore】一文让你学会IOS应用上架Appstore

咱们国内现在手机分为两类,Android手机与苹果手机,现在用的各类APP,为了手机的使用安全,避免下载到病毒软件,官方都极力推荐使用手机自带的应用商城进行下载,但是国内Android手机品类众多,手机商城各式各样,做不到统一,所以Android的APP上架得一个一个平台去申请上架,一直让开发人员头...

[转帖]Web技术(六):QUIC 是如何解决TCP 性能瓶颈的?

文章目录 一、QUIC 如何解决TCP的队头阻塞问题?1.1 TCP 为何会有队头阻塞问题1.2 QUIC 如何解决队头阻塞问题1.3 QUIC 没有队头阻塞的多路复用 二、QUIC 如何优化TCP 的连接管理机制?2.1 TCP连接的本质是什么2.2 QUIC 如何减少TCP 建立连接的开销2.3