设计模式之单例模式

Java 架构师深度解析单例模式

前言

在 Java 开发的世界里,单例模式(Singleton Pattern) 是所有设计模式中最简单、应用最广泛,也是面试必考的核心模式。作为一名 Java 架构师,我见过无数新手因为不懂单例导致生产环境出现资源泄漏、数据不一致、线程安全问题;也见过资深工程师用单例完美解决了全局配置、连接池、线程池等核心场景的架构难题。

单例模式是创建型设计模式的基石,它的核心思想只有一句话:保证一个类在整个 JVM 中仅有一个实例,并且提供一个全局访问该实例的入口

这篇文章我会带你从 0 到 1吃透单例模式:从通俗定义、核心特征,到 7 种由浅入深的代码实现;从结合七大设计原则的架构思维,到 JDK、Spring、MyBatis 源码中的实战;再到单例的破坏、防护、最佳实践和面试高频考点。全文通俗易懂,无晦涩术语,适合所有 Java 开发者学习。


第一章 单例模式基础认知(小白必看)

1.1 什么是单例模式?通俗定义

我们用生活中的例子理解单例:

  • 一个公司只有一个 CEO,所有人都找这同一个 CEO 汇报工作;
  • 一个家庭只有一个户口本,所有家庭成员共用这一个证件;
  • 一台电脑只有一个打印机,所有软件都调用这一个打印机打印文件。

映射到 Java 代码中:

一个类只能被创建一个对象,整个项目中所有地方使用的都是这同一个对象,不能 new 出第二个对象。这就是单例模式。

官方定义(GoF23 种设计模式):

单例模式确保一个类只有一个实例,并提供一个全局访问点来获取这个实例,同时禁止通过构造方法创建新实例。

1.2 单例模式的核心四大特征

所有合法的单例实现,都必须满足这 4 个特征,缺一不可:

  1. 私有构造方法(private constructor)

    这是单例的核心防线!将类的构造方法用private修饰,禁止外部通过new关键字创建对象,从根源上杜绝多实例。

  2. 私有静态实例变量(private static instance)

    static修饰实例变量,保证实例属于类本身,而不是对象;private修饰保证外部无法直接修改。

  3. 全局公开的访问方法(public static getInstance ())

    提供一个静态方法,作为外部获取单例对象的唯一入口。

  4. 唯一实例

    无论调用多少次getInstance(),返回的都是同一个对象,哈希码完全相同。

1.3 为什么必须使用单例模式?(3 大核心痛点)

如果不使用单例,随意创建对象,会引发 3 个致命问题:

1. 资源严重浪费

比如数据库连接池线程池日志对象

  • 每次创建连接池都要和数据库建立 TCP 连接,频繁创建会耗尽服务器资源;
  • 线程池创建会占用内存和 CPU,多实例线程池会导致线程泛滥。
  • 单例保证只创建一次,全局复用,极致节省资源。

2. 数据不一致

比如全局配置类缓存类

  • 如果有两个配置实例,一个修改了配置,另一个感知不到,导致业务逻辑错乱;
  • 多个缓存实例会导致缓存数据不统一,出现脏数据。

3. 全局状态失控

比如用户会话管理分布式锁实例

  • 多实例会导致全局状态无法统一管理,引发并发安全问题。

1.4 单例模式的经典应用场景(企业级开发必备)

在 Java 企业开发中,90% 的全局组件都用单例实现:

  1. 配置管理类:项目的全局配置(如数据库配置、Redis 配置);
  2. 资源池类:数据库连接池、线程池、HTTP 连接池;
  3. 工具类:日志工具、加密工具、文件上传工具;
  4. 缓存类:本地缓存、分布式缓存客户端;
  5. 框架核心类:Spring 的 Bean(默认单例)、MyBatis 的 SqlSessionFactory;
  6. 系统服务类:JDK 的RuntimeDesktop类。

1.5 单例模式的设计初衷与本质

单例模式的本质:控制实例数量 + 全局共享

它是为了解决全局唯一对象的创建和使用问题,是高内聚、低耦合的经典实践。


第二章 单例模式的 7 种实现方式(由浅入深・代码实战)

单例的实现分为两大类:

  • 饿汉式(Eager Initialization):类加载时就创建实例,线程安全,无延迟加载;
  • 懒汉式(Lazy Initialization):第一次使用时才创建实例,支持延迟加载,需要处理线程安全。

我会从最简单最优,逐一讲解 7 种实现,附带完整代码、优缺点、适用场景。


2.1 饿汉式(静态常量)- 最简单、最基础

核心思想

类加载的时候就直接创建实例,天生线程安全(JVM 类加载机制保证线程安全),代码极简。

代码实战

/**
 * 饿汉式单例(静态常量)- 最简单实现
 */
public class Singleton1 {
    // 1. 私有静态常量实例:类加载时就创建
    private static final Singleton1 INSTANCE = new Singleton1();

    // 2. 私有构造方法:禁止外部new
    private Singleton1() {}

    // 3. 全局公开访问方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}

测试代码

public class SingletonTest {
    public static void main(String[] args) {
        Singleton1 s1 = Singleton1.getInstance();
        Singleton1 s2 = Singleton1.getInstance();
        // 输出true,说明是同一个对象
        System.out.println(s1 == s2);
        // 输出相同的哈希码
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
    }
}

优点

  1. 代码最简单,无脑上手;
  2. JVM 类加载保证线程安全,无并发问题;
  3. 无锁,执行效率极高。

缺点

  1. 不支持延迟加载:即使项目不用这个实例,也会在类加载时创建,浪费内存;
  2. 如果实例初始化复杂,会拖慢类加载速度。

适用场景

实例小、初始化简单全局必定会使用的场景(比如工具类)。


2.2 饿汉式(静态代码块)- 变种,适合复杂初始化

核心思想

和静态常量饿汉式完全一致,只是把实例创建放在静态代码块中,适合需要复杂初始化逻辑的场景(比如读取配置文件、加载资源)。

代码实战

/**
 * 饿汉式单例(静态代码块)- 适合复杂初始化
 */
public class Singleton2 {
    // 1. 私有静态实例
    private static Singleton2 instance;

    // 2. 私有构造
    private Singleton2() {}

    // 3. 静态代码块:类加载时执行,创建实例
    static {
        // 这里可以写复杂初始化逻辑:读取配置、加载资源
        instance = new Singleton2();
    }

    // 4. 全局访问方法
    public static Singleton2 getInstance() {
        return instance;
    }
}

优缺点 / 适用场景

和静态常量饿汉式完全相同,仅多了复杂初始化的能力


2.3 懒汉式(线程不安全)- 延迟加载,入门级

核心思想

按需加载:第一次调用getInstance()时才创建实例,解决了饿汉式内存浪费的问题。

⚠️ 致命问题:多线程环境下完全不安全,会创建多个实例!

代码实战

/**
 * 懒汉式单例(线程不安全)- 禁止生产使用!
 */
public class Singleton3 {
    // 1. 私有静态实例:先不初始化
    private static Singleton3 instance;

    // 2. 私有构造
    private Singleton3() {}

    // 3. 全局访问方法:第一次调用时创建
    public static Singleton3 getInstance() {
        // 如果实例为空,才创建
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}

问题分析

多线程下,线程 A 和线程 B 同时判断instance == null,都会进入创建逻辑,最终生成两个不同的对象,破坏单例。

优点

支持延迟加载,节省内存。

缺点

线程不安全绝对不能用于生产环境

适用场景

仅用于单线程测试环境,无任何实际价值。


2.4 懒汉式(线程安全,同步方法)- 解决安全,性能差

核心思想

getInstance()方法上添加synchronized关键字,保证多线程下只有一个线程能进入方法,解决线程安全问题。

⚠️ 缺点:每次获取实例都要加锁,性能极差

代码实战

/**
 * 懒汉式单例(同步方法)- 线程安全,但性能低
 */
public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4() {}

    // 添加synchronized锁,保证线程安全
    public static synchronized Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }
}

优缺点

  1. 优点:线程安全、支持延迟加载;
  2. 缺点:锁粒度太大,高并发下所有线程都要排队获取实例,性能暴跌 90% 以上。

适用场景

低并发、对性能无要求的场景,企业开发极少使用


2.5 双重校验锁(DCL)- 高性能线程安全,主流

核心思想

Double-Check Locking,双重校验锁

  1. 第一次校验:无锁判断实例是否存在,避免每次加锁;
  2. 加锁:只有实例为空时才加锁创建;
  3. 第二次校验:防止多线程重复创建;
  4. volatile 关键字:禁止指令重排,保证线程安全。

这是高性能 + 延迟加载 + 线程安全的平衡方案,是早年企业开发的主流。

代码实战

/**
 * 双重校验锁(DCL)单例 - 高性能线程安全
 */
public class Singleton5 {
    // 1. volatile关键字:禁止指令重排,核心!
    private static volatile Singleton5 instance;

    private Singleton5() {}

    public static Singleton5 getInstance() {
        // 第一次校验:无锁,提高效率
        if (instance == null) {
            // 加锁:类锁
            synchronized (Singleton5.class) {
                // 第二次校验:防止多线程重复创建
                if (instance == null) {
                    instance = new Singleton5();
                }
            }
        }
        return instance;
    }
}

关键知识点:为什么必须加 volatile?

instance = new Singleton5() 不是原子操作,JVM 会分为 3 步:

  1. 分配内存空间;
  2. 初始化对象;
  3. 将 instance 指向内存空间。

JVM 会指令重排,可能执行顺序变成 1→3→2。

多线程下,线程 A 执行到 3,线程 B 判断instance != null,直接返回未初始化的对象,引发空指针异常。

volatile禁止指令重排,保证执行顺序 1→2→3,彻底解决问题。

优缺点

  1. 优点:线程安全、延迟加载、高性能;
  2. 缺点:代码稍复杂,存在指令重排风险(必须加 volatile)。

适用场景

高并发、需要延迟加载的场景。


2.6 静态内部类 - 简洁优雅,天生线程安全

核心思想

利用JVM 类加载机制实现线程安全和延迟加载:

  1. 外部类加载时,静态内部类不会被加载
  2. 第一次调用getInstance()时,静态内部类才会被加载,创建实例;
  3. JVM 保证类加载的线程安全性,无需加锁。

这是极简、高性能、线程安全的完美方案。

代码实战

/**
 * 静态内部类单例 - 优雅、天生线程安全
 */
public class Singleton6 {
    // 私有构造
    private Singleton6() {}

    // 1. 静态内部类:外部类加载时,内部类不会加载
    private static class SingletonHolder {
        // 2. 内部类中创建实例:JVM保证线程安全
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    // 3. 全局访问方法
    public static Singleton6 getInstance() {
        // 调用时才加载内部类,创建实例
        return SingletonHolder.INSTANCE;
    }
}

优缺点

  1. 优点:线程安全、延迟加载、无锁高性能、代码简洁;
  2. 缺点:无法传递参数初始化实例。

适用场景

绝大多数企业级场景,推荐优先使用


2.7 枚举单例 - 业界公认最优,防破坏

核心思想

《Effective Java》作者 Josh Bloch 推荐:枚举单例是单例模式的最佳实现

枚举天生:

  1. 只有一个实例;
  2. JVM 保证线程安全;
  3. 防止反射、序列化破坏单例(其他实现都做不到);
  4. 代码极简。

代码实战

/**
 * 枚举单例 - 业界公认最优实现
 */
public enum Singleton7 {
    // 唯一实例
    INSTANCE;

    // 单例的业务方法
    public void doSomething() {
        System.out.println("枚举单例执行业务逻辑");
    }
}

测试代码

public class SingletonTest {
    public static void main(String[] args) {
        Singleton7 s1 = Singleton7.INSTANCE;
        Singleton7 s2 = Singleton7.INSTANCE;
        System.out.println(s1 == s2); // true
        s1.doSomething();
    }
}

优缺点

  1. 优点:线程安全、防反射 / 序列化破坏、代码极简、性能最优
  2. 缺点:不支持延迟加载(类加载时创建)。

适用场景

所有企业级生产环境,架构师首选


2.8 扩展:容器式单例、ThreadLocal 单例

1. 容器式单例

适用于管理大量单例对象的场景(Spring 底层就是用容器单例):

import java.util.HashMap;
import java.util.Map;

/**
 * 容器式单例:统一管理多个单例
 */
public class ContainerSingleton {
    private static Map<String, Object> singletonMap = new HashMap<>();

    private ContainerSingleton() {}

    // 注册单例
    public static void registerInstance(String key, Object instance) {
        if (!singletonMap.containsKey(key)) {
            singletonMap.put(key, instance);
        }
    }

    // 获取单例
    public static Object getInstance(String key) {
        return singletonMap.get(key);
    }
}

2. ThreadLocal 单例

线程级单例:每个线程拥有一个独立实例,线程之间隔离:

/**
 * ThreadLocal单例:线程隔离的单例
 */
public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> THREAD_LOCAL =
            ThreadLocal.withInitial(ThreadLocalSingleton::new);

    private ThreadLocalSingleton() {}

    public static ThreadLocalSingleton getInstance() {
        return THREAD_LOCAL.get();
    }
}

2.9 7 种实现方式对比总结(选型表)

实现方式 线程安全 延迟加载 性能 防破坏 推荐指数
饿汉式(常量) ⭐⭐⭐
饿汉式(静态块) ⭐⭐⭐
懒汉式(不安全)
懒汉式(同步方法) 极低
双重校验锁 ⭐⭐⭐⭐
静态内部类 ⭐⭐⭐⭐⭐
枚举单例 ⭐⭐⭐⭐⭐⭐

第三章 单例模式与七大设计原则(架构师核心思维)

七大设计原则是架构设计的基石,单例模式作为最常用的设计模式,完美体现了这些原则的遵循与取舍

我会逐一结合单例模式,用通俗的语言讲解,让你真正理解架构设计的本质。

3.1 单一职责原则(SRP)

原则定义

一个类只负责一件事,只有一个引起它变化的原因。

单例模式的体现

完全遵循

单例类的核心职责只有两个:

  1. 创建自己的唯一实例
  2. 提供全局访问入口

它不参与业务逻辑,不处理数据,只负责实例的创建和管理,完美符合单一职责。

❌ 反例:

如果把单例类写成 “万能类”,既管理实例,又做数据库操作、日志打印、业务逻辑,就违背了单一职责。

3.2 开闭原则(OCP)

原则定义

扩展开放,对修改关闭。不修改原有代码,通过扩展实现新功能。

单例模式的体现

基础遵循

单例的实例管理逻辑固定,无需修改;可以通过继承、实现接口扩展单例的功能,不破坏原有代码。

局限性

  1. 饿汉式、静态内部类单例,实例创建逻辑写死,扩展需要修改代码;
  2. 枚举单例无法继承,扩展性最差。

架构优化

面向接口编程,将单例的业务逻辑抽象为接口,通过实现接口扩展功能,符合开闭原则。

3.3 里氏替换原则(LSP)

原则定义

子类可以完全替换父类,程序逻辑不变。父类能出现的地方,子类都能替换。

单例模式的体现

完全遵循

  1. 单例类可以作为父类,子类继承后,依然可以作为单例使用;
  2. 只要子类不破坏单例的核心特征(唯一实例),就可以无缝替换父类。

⚠️ 注意:

单例不推荐滥用继承,否则会破坏实例唯一性,违背里氏替换原则。

3.4 接口隔离原则(ISP)

原则定义

使用多个专门的接口,而不是一个臃肿的总接口。客户端不依赖不需要的方法。

单例模式的体现

遵循则优,违背则劣

  • 好的设计:单例类实现细粒度接口,只暴露需要的方法;
  • 坏的设计:单例类实现臃肿接口,包含大量无用方法,客户端被迫依赖不需要的功能。

示例

// 好的设计:细粒度接口
interface ConfigLoader {
    void loadConfig();
}
// 单例实现专用接口,符合接口隔离
public class ConfigSingleton implements ConfigLoader {}

3.5 依赖倒置原则(DIP)

原则定义

依赖抽象,不依赖具体。高层模块不依赖低层模块,二者都依赖抽象。

单例模式的体现

架构级遵循

企业开发中,我们不直接依赖单例类,而是依赖单例实现的接口:

// 抽象接口
interface CacheService {
    Object get(String key);
}
// 具体单例
public class RedisSingleton implements CacheService {}
// 高层依赖抽象,不依赖具体单例
public class UserService {
    private CacheService cacheService;
    // 依赖注入接口,符合依赖倒置
    public UserService(CacheService cacheService) {
        this.cacheService = cacheService;
    }
}

这是 Spring 框架的核心设计思想。

3.6 迪米特法则(LOD)- 最少知道原则

原则定义

一个类只和直接朋友通信,不和陌生人通信。最少暴露内部细节,降低耦合。

单例模式的体现

完美遵循

  1. 单例类私有构造,不暴露内部创建逻辑;
  2. 只暴露一个getInstance()方法,外部无需知道单例的创建细节;
  3. 外部只和getInstance()交互,耦合度极低。

3.7 合成复用原则(CRP)

原则定义

优先使用组合 / 聚合,而不是继承实现复用。

单例模式的体现

严格遵循

单例模式禁止滥用继承,因为继承会破坏实例唯一性;

推荐使用组合:将单例对象注入到其他类中使用,实现复用。

// 组合复用:将单例注入到业务类
public class OrderService {
    // 组合单例对象
    private final LogSingleton log = LogSingleton.getInstance();
}

3.8 总结:单例模式对七大原则的遵循与违背

原则 遵循情况 核心原因
单一职责 ✅ 完全遵循 只负责实例创建与管理
开闭原则 ⚠️ 部分遵循 枚举单例扩展性差
里氏替换 ✅ 完全遵循 子类可无缝替换父类
接口隔离 ✅ 可完全遵循 依赖细粒度接口即可
依赖倒置 ✅ 架构级遵循 面向接口编程,依赖抽象
迪米特法则 ✅ 完美遵循 最小暴露,低耦合
合成复用 ✅ 严格遵循 优先组合,禁止继承

核心结论:单例模式是最符合七大设计原则的设计模式之一,只要合理设计,就能实现高内聚、低耦合的架构。


第四章 企业级源码中的单例模式实战(吃透底层)

作为架构师,不仅要会写单例,更要读懂开源框架、JDK 源码中的单例设计,这是进阶的关键。

4.1 JDK 源码中的单例模式:Runtime 类

java.lang.Runtime是 JDK 自带的经典饿汉式单例,每个 JVM 进程只有一个 Runtime 实例,用于获取系统信息、执行系统命令。

源码截取

public class Runtime {
    // 饿汉式单例:静态常量
    private static Runtime currentRuntime = new Runtime();

    // 全局访问方法
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    // 私有构造:禁止外部创建
    private Runtime() {}
}

使用方式

public class RuntimeTest {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        // 获取JVM内存信息
        System.out.println("最大内存:" + runtime.maxMemory());
    }
}

4.2 Spring 框架中的单例模式:Bean 单例

Spring 的 Bean默认作用域是 singleton(单例),是企业开发中最常用的单例实现。

核心原理

Spring 用容器式单例管理 Bean:

  1. 通过SingletonBeanRegistry接口定义单例注册、获取规范;
  2. DefaultSingletonBeanRegistry实现,底层用Map<String, Object> singletonObjects缓存所有单例 Bean;
  3. 第一次获取 Bean 时创建,后续直接从缓存获取。

核心源码(简化)

public class DefaultSingletonBeanRegistry {
    // 单例缓存池:key=beanName,value=bean实例
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 获取单例Bean
    public Object getSingleton(String beanName) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 创建Bean,放入缓存
            singletonObject = createBean(beanName);
            this.singletonObjects.put(beanName, singletonObject);
        }
        return singletonObject;
    }
}

4.3 MyBatis 框架中的单例模式

MyBatis 的核心组件SqlSessionFactoryErrorContext都是单例:

  1. SqlSessionFactory:全局唯一,负责创建 SqlSession;
  2. ErrorContext:线程级单例,用 ThreadLocal 实现。

4.4 其他中间件的单例应用

  1. Tomcat:全局配置类、线程池都是单例;
  2. Redis 客户端:JedisPool 连接池是单例;
  3. Dubbo:注册中心、协议注册器都是单例。

第五章 单例模式的破坏与防护(高级必学)

除了枚举单例,其他所有单例实现都可以被破坏

作为架构师,必须掌握单例的4 种破坏方式防护手段

5.1 反射破坏单例:原理 + 代码 + 防护

破坏原理

反射可以无视私有构造方法,直接调用newInstance()创建新对象。

破坏代码

public class ReflectBreak {
    public static void main(String[] args) throws Exception {
        // 获取单例
        Singleton6 s1 = Singleton6.getInstance();
        // 反射获取构造方法
        Constructor<Singleton6> constructor = Singleton6.class.getDeclaredConstructor();
        // 无视私有构造
        constructor.setAccessible(true);
        // 创建新对象
        Singleton6 s2 = constructor.newInstance();
        // 输出false,单例被破坏
        System.out.println(s1 == s2);
    }
}

防护方案

在私有构造方法中判断实例是否已存在,存在则抛出异常:

private Singleton6() {
    if (SingletonHolder.INSTANCE != null) {
        throw new RuntimeException("禁止反射破坏单例!");
    }
}

5.2 序列化破坏单例:原理 + 代码 + 防护

破坏原理

序列化会把对象写入磁盘,反序列化时会创建新对象,破坏单例。

防护方案

单例类实现Serializable,重写readResolve()方法,直接返回单例实例:

private Object readResolve() {
    return SingletonHolder.INSTANCE;
}

5.3 克隆破坏单例:原理 + 代码 + 防护

破坏原理

clone()方法会创建一个新的对象,不走构造方法,破坏单例。

防护方案

  1. 单例类不实现 Cloneable 接口
  2. 若必须实现,重写clone()方法,抛出异常:
@Override
protected Object clone() throws CloneNotSupportedException {
    throw new RuntimeException("禁止克隆破坏单例!");
}

5.4 多类加载器破坏单例

破坏原理

不同类加载器加载同一个类,会创建多个 Class 对象,从而生成多个单例实例。

防护方案

指定统一的类加载器,保证类只被加载一次。


第六章 单例模式的优缺点与适用场景(精准选型)

6.1 单例模式的核心优点

  1. 资源最优利用:只创建一个实例,节省内存、CPU、连接资源;
  2. 全局状态统一:避免多实例导致的数据不一致;
  3. 全局访问方便:无需传递对象,随处调用;
  4. 线程安全可控:合理实现可保证高并发下的安全。

6.2 单例模式的核心缺点

  1. 扩展性差:单例类很难扩展,违背开闭原则;
  2. 测试困难:单例的全局状态会导致单元测试相互干扰;
  3. 隐藏依赖:单例无需注入,容易隐藏类之间的依赖关系;
  4. 生命周期过长:单例伴随 JVM 全程,容易引发内存泄漏。

6.3 单例模式的适用场景

  1. 全局唯一的资源(连接池、线程池、配置);
  2. 无状态的工具类;
  3. 框架核心组件;
  4. 需要全局共享数据的场景。

6.4 单例模式的反适用场景

  1. 有状态的业务对象;
  2. 需要频繁创建销毁的对象;
  3. 需要多实例隔离的场景;
  4. 追求高扩展性的模块。

第七章 单例模式最佳实践(架构师经验)

7.1 企业开发选型建议

  1. 首选枚举单例:简单、安全、防破坏,适合绝大多数场景;
  2. 需要延迟加载选静态内部类:高性能、优雅;
  3. 高并发复杂场景选双重校验锁
  4. 小实例、必用场景选饿汉式

7.2 单例类的设计规范

  1. 构造方法必须私有,且添加防护逻辑;
  2. 禁止在单例中存储可变状态;
  3. 单例类保持精简,符合单一职责;
  4. 面向接口编程,依赖抽象不依赖具体。

7.3 单例与依赖注入(DI)结合

Spring 的 DI + 单例是最佳实践:

  • 用 Spring 管理单例 Bean,无需手动写单例代码;
  • 依赖注入解决单例的隐藏依赖问题;
  • 方便单元测试替换单例实现。

第八章 单例模式高频面试题(通关必备)

8.1 基础面试题

  1. 什么是单例模式?核心特征是什么?
  2. 饿汉式和懒汉式的区别?
  3. 单例模式的应用场景?

8.2 中级面试题

  1. 双重校验锁为什么必须加 volatile?
  2. 静态内部类单例的原理?
  3. 如何防止反射破坏单例?

8.3 高级面试题

  1. 为什么枚举单例是最优实现?
  2. Spring 单例 Bean 的线程安全问题?
  3. 多线程、多类加载器下如何保证单例?

结语

单例模式看似简单,实则蕴含了JVM 类加载、线程安全、设计原则、架构设计等核心知识。作为 Java 开发者,单例是你必须吃透的第一个设计模式,也是通往架构师之路的基石。

核心总结

  1. 单例的本质:唯一实例 + 全局访问
  2. 最优实现:枚举单例
  3. 架构核心:遵循七大设计原则,面向接口编程
  4. 企业实践:结合 Spring 依赖注入,杜绝手动写单例

希望这篇万字长文,能让你真正掌握单例模式,在开发、面试、架构设计中得心应手!

posted @ 2026-03-31 20:21  bright_ye  阅读(15)  评论(0)    收藏  举报