news 2026/5/23 2:38:41

Java 中单例对象写法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 中单例对象写法

单例模式的核心原则

实现单例的核心要求:

  1. 私有构造方法(防止外部通过new创建实例);
  2. 类内部创建唯一实例;
  3. 提供公共静态方法获取该实例;
  4. 保证多线程环境下实例唯一(线程安全)。

写法 1:饿汉式(静态常量)—— 最简单的线程安全单例

代码实现
/** * 饿汉式(静态常量) * 特点:线程安全、无懒加载、类加载时就初始化实例 */ public class Singleton1 { // 1. 私有构造方法,禁止外部new private Singleton1() {} // 2. 类加载时就创建唯一实例(饿汉:提前初始化) private static final Singleton1 INSTANCE = new Singleton1(); // 3. 提供公共方法获取实例 public static Singleton1 getInstance() { return INSTANCE; } }
解析
  • 线程安全:类加载过程由 JVM 保证线程安全(静态变量初始化在类加载的<clinit>方法中,JVM 会保证该方法被加锁,多线程下只会执行一次);
  • 优点:实现简单、无线程安全问题、性能高;
  • 缺点:无懒加载(类加载时就初始化实例),如果实例创建成本高(如占内存大、依赖外部资源),且程序全程未使用该实例,会造成资源浪费;
  • 适用场景:实例创建成本低、程序启动后大概率会使用的单例。

写法 2:饿汉式(静态代码块)—— 初始化逻辑复杂的饿汉式

代码实现
/** * 饿汉式(静态代码块) * 特点:线程安全、无懒加载,适合初始化逻辑复杂的场景 */ public class Singleton2 { private Singleton2() {} // 先声明实例,静态代码块中初始化(适合需要读取配置文件等复杂逻辑) private static Singleton2 INSTANCE; static { // 可添加复杂初始化逻辑,如读取配置、连接数据库等 INSTANCE = new Singleton2(); } public static Singleton2 getInstance() { return INSTANCE; } }
解析
  • 核心逻辑和「静态常量饿汉式」一致,只是把实例初始化放到静态代码块中;
  • 适用场景:单例初始化需要执行复杂逻辑(如读取配置文件、初始化资源)时。

写法 3:懒汉式(线程不安全)—— 笔试常考 “反面教材”

代码实现
/** * 懒汉式(线程不安全) * 特点:懒加载(使用时才初始化),但多线程下会创建多个实例,实际开发禁止使用 */ public class Singleton3 { private Singleton3() {} private static Singleton3 INSTANCE; // 调用方法时才初始化(懒加载) public static Singleton3 getInstance() { if (INSTANCE == null) { // 多线程下此处会并发执行,创建多个实例 INSTANCE = new Singleton3(); } return INSTANCE; } }
解析
  • 线程不安全:多线程同时调用getInstance()时,会同时进入if (INSTANCE == null),创建多个实例,破坏单例;
  • 优点:懒加载,节省资源;
  • 缺点:线程不安全,仅适用于单线程环境;
  • 笔试考点:面试官常问 “这个写法有什么问题?”,需答出 “多线程下会创建多个实例”。

写法 4:懒汉式(线程安全,同步方法)—— 安全但低效

代码实现
/** * 懒汉式(同步方法) * 特点:线程安全、懒加载,但每次调用getInstance都加锁,效率低 */ public class Singleton4 { private Singleton4() {} private static Singleton4 INSTANCE; // 加synchronized保证线程安全,但每次调用都锁整个方法,效率低 public static synchronized Singleton4 getInstance() { if (INSTANCE == null) { INSTANCE = new Singleton4(); } return INSTANCE; } }
解析
  • 线程安全synchronized修饰静态方法,保证多线程下只有一个线程能进入方法;
  • 优点:懒加载、线程安全;
  • 缺点:效率极低 —— 即使实例已创建,后续所有调用getInstance()的线程都要排队获取锁,浪费资源;
  • 适用场景:单例调用频率极低的场景(几乎不用)。

写法 5:双重校验锁(DCL)—— 实际开发最常用

代码实现
/** * 双重校验锁(DCL,Double Check Lock) * 特点:线程安全、懒加载、效率高,实际开发首选 */ public class Singleton5 { private Singleton5() {} // 关键:volatile修饰,防止指令重排序 private static volatile Singleton5 INSTANCE; public static Singleton5 getInstance() { // 第一次校验:实例已创建则直接返回,无需加锁(提高效率) if (INSTANCE == null) { synchronized (Singleton5.class) { // 锁类对象,保证只有一个线程进入 // 第二次校验:防止多个线程等待锁后重复创建实例 if (INSTANCE == null) { INSTANCE = new Singleton5(); } } } return INSTANCE; } }
核心解析(笔试高频考点)
  1. 为什么需要两次校验?

    • 第一次校验(外层if):实例已创建时,直接返回,避免进入同步块,提高效率;
    • 第二次校验(内层if):假设两个线程同时通过外层if,等待锁后,只有一个线程能进入同步块,第二个线程进入时,实例已创建,避免重复初始化。
  2. 为什么需要 volatile?

    • INSTANCE = new Singleton5()不是原子操作,JVM 会拆分为 3 步:① 分配内存空间;② 初始化实例;③ 把 INSTANCE 指向分配的内存地址。
    • 若无 volatile,JVM 可能发生指令重排序(①→③→②),导致:线程 A 执行完①③,INSTANCE 已非 null,但实例未初始化;线程 B 进入外层if,发现 INSTANCE≠null,直接返回未初始化的实例,引发空指针异常。
    • volatile 禁止指令重排序,保证①→②→③的执行顺序,避免上述问题。
优缺点
  • 优点:线程安全、懒加载、效率高(仅初始化时加锁,后续无锁);
  • 缺点:实现稍复杂,需注意 volatile 不能省略;
  • 适用场景:绝大多数实际开发场景(如工具类、连接池单例)。

写法 6:静态内部类 —— 优雅的懒加载单例

代码实现
/** * 静态内部类单例 * 特点:线程安全、懒加载、实现优雅,《Effective Java》推荐写法之一 */ public class Singleton6 { private Singleton6() {} // 静态内部类:不会随外部类加载而初始化,只有调用getInstance时才加载 private static class SingletonHolder { // JVM保证静态常量初始化的线程安全 private static final Singleton6 INSTANCE = new Singleton6(); } public static Singleton6 getInstance() { // 调用此方法时,才加载SingletonHolder,创建INSTANCE return SingletonHolder.INSTANCE; } }
解析
  • 懒加载:外部类Singleton6加载时,内部类SingletonHolder不会加载;只有调用getInstance()时,内部类才加载,创建实例;
  • 线程安全:JVM 保证静态内部类的<clinit>方法线程安全,实例只会创建一次;
  • 优缺点
    • 优点:线程安全、懒加载、实现优雅(无需手动加锁 /volatile);
    • 缺点:无法防止反射破坏单例(所有非枚举单例都有此问题)。

写法 7:枚举单例 —— 最安全的单例(《Effective Java》推荐)

代码实现
/** * 枚举单例 * 特点:天然线程安全、防止反射/序列化破坏单例,最简单的完美单例 */ public enum Singleton7 { // 唯一实例(枚举的每个常量都是单例) INSTANCE; // 可添加单例的业务方法 public void doSomething() { System.out.println("枚举单例执行方法"); } } // 使用方式 // Singleton7.INSTANCE.doSomething();
核心优势(笔试必答)
  1. 天然线程安全:枚举类的初始化由 JVM 保证,多线程下只会创建一个实例;
  2. 防止反射破坏:反射无法创建枚举实例(Constructor.newInstance()会抛异常);
  3. 防止序列化破坏:枚举的序列化由 JVM 处理,反序列化不会创建新实例;
  4. 实现最简单:无需手动处理锁、volatile 等,代码极简。
优缺点
  • 优点:绝对线程安全、防反射 / 序列化、实现简单;
  • 缺点:无懒加载(枚举类加载时就初始化实例);
  • 适用场景:需要绝对安全的单例场景(如配置中心、核心工具类)。

总结

写法线程安全懒加载防反射 / 序列化适用场景
饿汉式(静态常量)实例创建成本低、必用的单例
懒汉式(同步方法)调用频率极低的单例(几乎不用)
双重校验锁(DCL)绝大多数实际开发场景
静态内部类追求优雅实现的懒加载单例
枚举单例要求绝对安全的单例

关键点回顾

  1. 笔试高频考点:DCL 写法中 volatile 的作用、双重校验的原因、枚举单例的优势;
  2. 实际开发优先选:DCL(需懒加载)、枚举单例(需绝对安全)、静态内部类(追求优雅);
  3. 所有非枚举单例的构造方法私有,仍可通过反射暴力破解(设置setAccessible(true)),枚举单例可彻底避免。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 23:48:36

28.图层和混合模式 (Layers and Blend Modes)

图层混合效果&#xff0c;视觉特效的实现&#xff0c;创意设计的支持&#x1f4d6; 章节概述CSS图层和混合模式为Web设计带来了强大的视觉效果能力。通过mix-blend-mode和background-blend-mode属性&#xff0c;我们可以创建类似Photoshop的图层混合效果&#xff0c;实现丰富的…

作者头像 李华
网站建设 2026/5/22 23:40:42

快速使用开发者空间AI Agent打造你的私人营养师

本篇讲带你快速使用Agent部署&#xff0c;教你把“今天吃什么”现成MCP模板一键装进Agent&#xff0c;5分钟完成安装-配置-发布三连&#xff0c;现场就能在浏览器里跟AI营养师聊菜单。 华为开发者空间的Agent到底是什么&#xff1f; 简单来说&#xff0c;Agent开发平台(AI原生…

作者头像 李华
网站建设 2026/5/12 9:40:41

Android onReceive方法详解:使用教程与常见问题

BroadcastReceiver的onReceive方法是Android开发中处理广播的核心机制。无论是系统事件如网络状态变化、电量不足&#xff0c;还是应用内自定义的广播消息&#xff0c;都需要通过这个方法进行响应。理解onReceive的工作方式、生命周期限制和最佳实践&#xff0c;对于编写稳定高…

作者头像 李华
网站建设 2026/5/11 2:29:28

收藏必读:小公司搞大模型,别碰训练微调,学会使用才是关键

收藏必读&#xff1a;小公司搞大模型&#xff0c;别碰训练微调&#xff0c;学会使用才是关键 文章指出小公司不应进行大模型训练与微调&#xff0c;因其技术复杂、成本高昂&#xff0c;需大量数据、算力和专业理解&#xff0c;小公司难以具备。大模型技术迭代迅速&#xff0c;…

作者头像 李华
网站建设 2026/5/19 6:45:33

收藏备用|程序员/小白转行大模型全攻略,从零入门不踩坑

对于想入局大模型领域的程序员、零基础小白来说&#xff0c;最头疼的莫过于“不知从何学起”“学了不会用”。本文整理了一份超详细的大模型转型指南&#xff0c;从方向选择到实践落地&#xff0c;从学习资源到职业规划&#xff0c;手把手帮你从零突破&#xff0c;快速跻身大模…

作者头像 李华