重生之我在Java世界------学单例设计模式

devtools/2024/9/25 3:39:12/

什么是单例设计模式

单例模式是面向对象编程中最简单却又最常用的设计模式之一。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的原理、常见实现方法、优缺点,以及在使用过程中可能遇到的陷阱。

单例模式的核心原理

单例模式的实现主要依赖于以下三个要素:

  1. 私有构造函数:防止外部直接创建实例。
  2. 私有静态实例:类的唯一实例。
  3. 公共静态访问方法:提供全局访问点。

这种设计确保了在整个应用程序中,特定的类只会有一个实例存在。单例模式常用于管理共享资源、全局配置或需要统一协调行为的场景。

常见实现方法

懒汉式(线程不安全)

java">public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

这种方法实现了延迟加载,即在第一次调用 getInstance() 方法时才创建实例。然而,它在多线程环境下是不安全的。如果多个线程同时调用 getInstance() 方法,可能会创建多个实例。

饿汉式

java">public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}

饿汉式在类加载时就创建了实例,因此天然线程安全。但它没有实现延迟加载,可能会造成资源浪费。

双重检查锁

java">public class Singleton {private volatile static Singleton instance;private Singleton() {}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 SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}

静态内部类方法既实现了**延迟加载,又保证了线程安全。**它利用了Java的类加载机制来保证只创建一个实例。当 Singleton 类被加载时,SingletonHolder 类并不会被立即初始化,只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,从而创建 INSTANCE。这种方法也被称为 Initialization on Demand Holder (IODH) 模式。

枚举

java">public enum Singleton {INSTANCE;public void doSomething() {// 方法实现}
}

枚举实现是最简洁的单例模式实现方式。它不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象。。

单例模式的优缺点

**单例模式的主要优点在于它能够确保一个类只有一个实例,提供了对该实例的全局访问点,**并且可以显著节省系统资源。然而,它也存在一些缺点。单例类可能会违反单一职责原则,因为它不仅要管理自己的功能,还要确保自己是唯一实例。此外,单例模式在某些情况下可能会使单元测试变得困难,因为很难模拟单例类的不同状态。

单例模式的潜在陷阱(拓展)

反射机制破坏单例

Java的反射机制可以用来破坏单例。通过反射,可以强制调用私有构造函数,从而创建多个实例。例如:

java">Singleton instance1 = Singleton.getInstance();
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton instance2 = constructor.newInstance();

为了防止这种情况,可以在构造函数中添加检查,如果实例已经存在,则抛出异常。枚举实现的单例可以有效防止反射攻击。

序列化破坏单例

如果单例类是可序列化的,那么在反序列化时会创建新的实例。为了防止这种情况,可以实现 readResolve() 方法:

java">private Object readResolve() {return getInstance();
}

同样,枚举实现的单例也天然地防止了序列化问题。

多个类加载器

在使用多个类加载器的环境中,可能会出现多个单例实例。这种情况比较少见,但在复杂的应用服务器环境中可能会遇到。解决方法包括使用上下文类加载器或将单例类放在共享的类路径中。

结语

单例模式虽然概念简单,但在实际应用中需要考虑诸多因素,如线程安全、延迟加载、序列化等。选择合适的实现方法并注意潜在的陷阱,对于正确使用单例模式至关重要。在使用单例模式时,应该根据具体的应用场景和需求,权衡其利弊,做出最适合的选择。

从上述几种实现方法来看,静态内部类和枚举实现都提供了很好的平衡:它们既保证了线程安全,又兼顾了延迟加载(静态内部类)或简洁性(枚举)。特别是枚举实现,它还额外提供了防止反射攻击和序列化问题的保护。


http://www.ppmy.cn/devtools/116788.html

相关文章

CTF 技能树 LOG -GIT泄露 笔记

log 使用虚拟机kali操作 python2 安装 apt-get install python2 进入root用户&#xff0c;下载克隆git hack库 git clone https://github.com/BugScanTeam/GitHack sudo passwd root 修改root 命名密码为root 切换登录 su root 终端进入home/kali/GitHack/ python GitH…

mac 怎么查看CPU核数

在 macOS 系统中&#xff0c;可以通过以下几种方法查看 CPU 核心数&#xff1a; 1. 使用“关于本机”查看 点击左上角的苹果图标&#xff08;&#xff09;。选择“关于本机”。在弹出的窗口中&#xff0c;系统会显示 Mac 的基本信息&#xff0c;包括 CPU 的类型和核心数。比…

6个Python小游戏项目源码【免费】

6个Python小游戏项目源码 源码下载地址&#xff1a; 6个Python小游戏项目源码 提取码: bfh3

对称密码中的密钥是如何实现安全配送的?

对称密码在设计时就存在一个天然的缺陷&#xff0c;就是要求通信双方都要持有相同的密钥。确保密钥的安全传输和防止密钥泄露&#xff0c;往往比加密算法本身更为复杂和困难。一旦密钥被第三方获取&#xff0c;通信的安全性就会受到严重威胁&#xff0c;从而可能暴露敏感信息。…

Mac 上,终端如何开启 proxy

文章目录 为什么要这么做前提步骤查看 port查看代理的port配置 bash测试 为什么要这么做 mac 上的终端比较孤僻吧&#xff0c;虽然开了&#xff0c;但是终端并不走&#x1fa9c;…产生的现象就是&#xff0c;浏览器可以访问&#x1f30d;&#xff0c;但是终端不可以访问&#…

大数据Flink(一百二十):Flink SQL自定义函数(UDF)

文章目录 Flink SQL自定义函数&#xff08;UDF&#xff09; 一、概述 二、​​​​​​​自定义标量函数&#xff08;UDSF&#xff09; 三、​​​​​​​​​​​​​​自定义聚合函数(UDAF) 四、 ​​​​​​​​​​​​​​自定义表值函数(UDTF) Flink SQL自定义函数…

自动化学习3:日志记录及测试报告的生成--自动化框架搭建

一.日志记录 1.配置文件pytest.ini&#xff1a;将日志写入文件方便日后查询或查看执行信息。 需要将文件处理器&#xff08;文件存放位置/时间/格式等等&#xff09;添加到配置文件中的【日志记录器】 # pytest.ini [pytest] # ---------------日志文件&#xff0c;需要配合…

力扣算法题总结

lc253 题目&#xff1a;求最多重叠(x,y)的数量 思路&#xff1a;按y排序&#xff0c;把y放入优先队列&#xff0c;逐个比较x&#xff0c;x大于优先队列的堆顶元素就弹出堆顶。 lc148 题目&#xff1a;对链表排序 思路&#xff1a;归并排序。快慢指针找到链表中点&#xff0c…