news 2026/8/22 16:29:19

面试官:单例模式有几种写法?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
面试官:单例模式有几种写法?

在线 Java 面试刷题(持续更新):https://www.quanxiaoha.com/java-interview

面试考察点

面试官提出这个问题,主要想考察以下几个层面,而不仅仅是让你罗列名称:

  1. 对单例模式核心思想与实现细节的理解深度:你是否真正理解 “保证一个类仅有一个实例,并提供一个全局访问点” 这一核心目标。

  2. 对多线程环境下并发安全的掌握:如何确保在高并发场景下,单例的创建依然是线程安全的,这是考察重点。

  3. 对 JVM 类加载机制、内存模型(如volatile)、反射等底层知识的理解:不同实现方式背后的原理是什么?为何有些写法是安全的,有些则存在隐患。

  4. 实际工程应用与设计权衡能力:你是否了解每种写法的优缺点、适用场景,以及目前公认的最佳实践是什么。这能反映你的工程经验和代码品味。

核心答案

单例模式的主流写法通常可分为5 种饿汉式懒汉式(线程不安全与同步方法版)双重检查锁(DCL)静态内部类(Holder)以及枚举(Enum)

安全性、简洁性、功能性综合来看,枚举(Enum)是实现单例模式的最佳实践,它由 JVM 从根本上保证线程安全、反射安全和序列化/反序列化安全。静态内部类则是延迟加载场景下非枚举方式的首选。

深度解析

单例模式看似简单,但 “写对” 并 “写好” 需要考虑诸多细节。下面我们逐一拆解。

1. 饿汉式(Eager Initialization)

  • 原理:在类加载的初始化阶段,就通过静态变量创建实例。这利用了 JVM 类加载机制的线程安全性来保证实例唯一。

  • 代码示例:

    public class EagerSingleton { // 1. 私有静态常量,类加载时即初始化 private static final EagerSingleton INSTANCE = new EagerSingleton(); // 2. 私有构造函数 private EagerSingleton() {} // 3. 公有静态方法,返回唯一实例 public static EagerSingleton getInstance() { return INSTANCE; } }
  • 优劣分析:

    • 优点:实现简单,线程安全(由 JVM 保证)。

    • 缺点非延迟加载。无论是否用到,实例都在启动时创建,若实例创建耗资源或始终未使用,则会造成资源浪费。

  • 适用场景:单例实例较小,且程序启动后立即会使用的场景。

2. 懒汉式(Lazy Initialization)

  • 基础(线程不安全)版:在getInstance()中判断并创建,多线程下会创建多个实例,严禁使用。

    // ❌ 错误示例:线程不安全 public static LazySingleton getInstance() { if (instance == null) { // 多个线程可能同时进入此判断 instance = new LazySingleton(); } return instance; }
  • 同步方法(线程安全)版:在方法上直接加synchronized锁。

    public class SynchronizedSingleton { private static SynchronizedSingleton instance; private SynchronizedSingleton() {} // 使用 synchronized 保证线程安全 public static synchronized SynchronizedSingleton getInstance() { if (instance == null) { instance = new SynchronizedSingleton(); } return instance; } }
    • 缺点:锁粒度太粗,每次调用getInstance()都需要同步,性能低下

3. 双重检查锁(Double-Checked Locking, DCL)

  • 原理:在懒汉式的基础上,将同步块的范围缩小,并在同步块内外进行两次判空检查。结合volatile关键字(JDK 5+)防止指令重排序导致的 “部分初始化” 问题。

  • 代码示例(标准写法):

    public class DCLSingleton { // 必须使用 volatile 防止指令重排 privatestaticvolatile DCLSingleton instance; private DCLSingleton() {} public static DCLSingleton getInstance() { if (instance == null) { // 第一次检查,避免不必要的同步 synchronized (DCLSingleton.class) { if (instance == null) { // 第二次检查,确保唯一 instance = new DCLSingleton(); // 依赖 volatile 保证写操作先行发生 } } } return instance; } }
  • 最佳实践与常见误区:

    • volatile关键字必不可少。没有它,线程可能拿到一个未初始化完全的对象(半初始化状态)。

    • 这是延迟加载且性能较好的方案,但代码稍显复杂。

4. 静态内部类(Static Inner Class / Holder)

  • 原理:利用 JVM 的类加载机制 —— 静态内部类只有在被主动引用时(如调用getInstance())才会加载,从而实现延迟加载。其静态变量初始化由 JVM 保证线程安全。

  • 代码示例:

    public class InnerClassSingleton { private InnerClassSingleton() {} // 静态内部类 private static class SingletonHolder { private static final InnerClassSingleton INSTANCE = new InnerClassSingleton(); } public static InnerClassSingleton getInstance() { return SingletonHolder.INSTANCE; // 触发内部类加载和初始化 } }
  • 优劣分析:

    • 优点:实现简洁,线程安全,延迟加载,性能好(无锁)。是非枚举方式中的首选

    • 缺点:无法防止通过反射调用私有构造函数创建新实例(但可以通过在构造器中加判断来防御)。

5. 枚举(Enum)【最佳实践】

  • 原理:Java 枚举类型本身的特性保证了其常量(即单例实例)在 JVM 中是唯一的,且其构造器是私有的。JVM 从根本上保证了线程安全、反射安全(JDK 内部禁止使用反射创建枚举实例)和序列化安全(枚举的序列化机制特殊,仅存储名字,反序列化时通过valueOf获取同名常量)。

  • 代码示例:

    public enum EnumSingleton { INSTANCE; // 单例实例 // 可以添加任意方法和属性 public void doSomething() { System.out.println("Singleton Business Method."); } } // 使用:EnumSingleton.INSTANCE.doSomething();
  • 最佳实践:

    • 《Effective Java》作者 Josh Bloch 强烈推荐的方式

    • 代码极度简洁,且能抵御反射和序列化的攻击,是功能最完备的单例实现。

总结

实现单例模式的关键在于确保线程安全、实现延迟加载(按需创建)并防御反射与序列化破坏。在无特殊需求时,优先使用枚举(Enum)实现;若因历史原因不能使用枚举,静态内部类(Holder)是实现延迟加载单例的优秀选择。

👉 欢迎加入小哈的星球,你将获得:专属的项目实战(多个项目) / 1v1 提问 /Java 学习路线 /学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《Spring AI 项目实战》正在更新中..., 基于 Spring AI + Spring Boot 3.x + JDK 21;

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍;演示地址:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》2期已完结,演示链接:http://116.62.199.48/;

  • 专栏阅读地址:https://www.quanxiaoha.com/column

截止目前,累计输出 100w+ 字,讲解图 4013+ 张,还在持续爆肝中..后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有4200+小伙伴加入

1. 我的私密学习小圈子,从0到1手撸企业实战项目~ 2. 公司大佬对 Excel 导入、导出的封装,那叫一个妙啊! 3. 面试官:设计模式的 7 大基本原则有哪些? 4. SpringBoot 4 最被低估的新特性:Spring Data AOT
最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。 获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。 点“在看”支持小哈呀,谢谢啦
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 16:29:04

MT5效果展示:输入一句话,AI帮你裂变出5种不同说法

MT5效果展示:输入一句话,AI帮你裂变出5种不同说法 1. 引言:当一句话需要“七十二变” 你有没有遇到过这样的困境?写了一段文案,总觉得表达不够出彩;准备了一份报告,翻来覆去都是那几个词&…

作者头像 李华
网站建设 2026/7/14 16:39:56

XHS-Downloader:解决小红书素材采集难题的无水印批量下载方案

XHS-Downloader:解决小红书素材采集难题的无水印批量下载方案 【免费下载链接】XHS-Downloader 免费;轻量;开源,基于 AIOHTTP 模块实现的小红书图文/视频作品采集工具 项目地址: https://gitcode.com/gh_mirrors/xh/XHS-Downloa…

作者头像 李华
网站建设 2026/8/22 16:29:18

2025 VSCode下.NET开发环境配置与调试实战指南

1. 为什么选择VSCode来写.NET代码? 如果你和我一样,是从那个“庞然大物”Visual Studio转战过来的,心里肯定犯过嘀咕:VSCode这么轻量,真能搞定我习惯了的那些强大功能吗?尤其是调试,那可是开发者…

作者头像 李华
网站建设 2026/7/14 16:39:54

R 4.5部署危机预警(2024年Q3已触发17起CRAN包兼容性熔断):紧急绕过reticulate与future冲突的5种军工级方案

第一章:R 4.5机器学习模型部署的危机本质与全局影响R 4.5版本引入了严格的符号绑定机制与S3方法分派重构,导致大量依赖动态环境注入(如 assign()、eval(parse()))的旧有部署脚本在生产环境中静默失效。这种失效并非报错中断&#…

作者头像 李华
网站建设 2026/7/14 16:40:13

基于国产MCU的高精度USB电流表设计

1. 项目概述USB接口作为现代电子设备最普及的供电与通信接口,其电流参数直接关系到设备功耗评估、电源适配器选型、线缆可靠性验证及故障诊断等实际工程需求。在产线测试、实验室调试或现场维修场景中,工程师常需快速获取被测设备在不同工作状态下的实时…

作者头像 李华