news 2026/4/16 7:31:35

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/15 16:41:36

打造自己的大模型-02篇|LoRA微调大模型的评测和导出

在之前的的文章《打造自己的大模型&#xff5c;01篇LLaMA-Factory微调Llama3和其占用资源分析》&#xff0c;我们通过训练了9.5小时&#xff0c;完成了Llama3-8B-Instruct的LoRA 微调训练。 本篇文章&#xff0c;我们将继续利用LLaMA-Factory&#xff0c;进行大模型的评测和导出…

作者头像 李华
网站建设 2026/4/15 12:35:56

别卷Prompt了!Agent才是大模型时代程序员的终极外挂

MCP、A2A两个词在AI界火爆&#xff01;你都知道是什么吗&#xff1f; **2025 年&#xff0c;**注定是 Agent 从技术概念走向商业主流的转折点。无论是企业还是个人&#xff0c;若想在这场智能化浪潮中不被淘汰&#xff0c;拥抱 Agent 已不再是选择题&#xff0c;而是生存题。 …

作者头像 李华
网站建设 2026/3/13 10:41:57

Shell脚本if elif语法与MySQL数据库操作实用教程

在Shell脚本中结合条件判断与MySQL操作&#xff0c;是自动化运维和数据处理中的常见需求。通过if-elif-else结构&#xff0c;我们可以根据不同的条件执行相应的数据库操作&#xff0c;实现流程控制和错误处理。掌握这一组合技能&#xff0c;能有效提升脚本的健壮性和实用性。 S…

作者头像 李华
网站建设 2026/4/14 23:29:06

【异常】Antigravity IDE 登录异常与网络连接错误排查指南

在使用 Google 推出的 AI 原生 IDE Antigravity 时,部分用户可能会遇到身份验证失效与网络环境冲突导致的叠加错误。本文将针对此类典型故障提供完整的解决思路。 一、 报错内容 在 IDE 的通知栏或输出面板中,通常会同时出现以下两条错误提示: 网络连接错误: Check your i…

作者头像 李华
网站建设 2026/4/15 7:58:10

库克反击中国手机,大量安卓用户转买iPhone,真是风水轮流转!

在以往中国手机品牌都喜欢说苹果用户舍弃iPhone购买他们的手机&#xff0c;苹果往往都不会回应&#xff0c;而这次苹果CEO库克专门提到中国市场“从安卓阵营转入的用户数量创下新高”&#xff0c;这无疑就是在回敬中国手机。库克当然有理由高兴&#xff0c;因为2025年四季度苹果…

作者头像 李华