当前位置: 首页 > news >正文

Java单例模式

确保每个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式避免了状态不一致的情况。

特点:

·单例类只有一个实例;

·单例类自己创建那个唯一的实例;

·单例类为整个系统的其他对象提供这一实例。

单例模式保证了全局对象的唯一性。例如配置类等。

单例的四大原则:

·构造私有;

·以静态方法或者枚举方法返回实例;

·确保只有一个实例,尤其是多线程环境;

·确保反序列化不会构建对象。

实现实例化的方法有:

饿汉式(立即加载)

public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } private static Singleton single = new Singleton(); public static Singleton getInstance() { System.out.println("getInstance"); return single; } }

懒汉式(延迟加载)

public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } private static Singleton single=null; public static Singleton getInstance() { System.out.println("getInstance"); if(single==null){ single = new Singleton(); } return single; } }

同步锁(解决多线程问题)

public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } private static Singleton single=null; public synchronized static Singleton getInstance() { System.out.println("getInstance"); if(single==null){ single = new Singleton(); } return single; } }

双重检查锁(解决多线程下使用同步锁的性能问题)

为了禁止指令重排序,保证对象在“引用被赋值”之前“已经完全初始化”,实例变量要使用volatile修饰。

在JVM中,执行 single= new Singleton()这行代码,在字节码层面并不是原子操作,而是分为三个步骤:

  1. 分配内存:在堆中开辟一块内存空间(此时内存中全是默认值,比如int=0,引用=null)。

  2. 初始化对象:调用构造器,将内存中的值填充为真正的初始值(比如int=5,引用指向具体对象)。

  3. 建立关联(赋值):将堆内存的地址赋值给栈中的 single 引用变量。

不使用volatile修饰可能会导致步骤2未执行,步骤3已经执行,其他线程会判断single 不为null而直接使用,但是single并未初始化而导致空指针异常。

public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } private volatile static Singleton single=null; public static Singleton getInstance() { System.out.println("getInstance"); if(single==null){ synchronized (Singleton.class) { if(single==null) { single = new Singleton(); } } } return single; } }

内部静态类(懒汉式,延迟加载,线程安全,但是不能解决反序列化下重新构建实例的问题)

原理:延迟加载和JVM类加载锁(线程安全)

唯一的实例化时机:Java类加载的“初始化阶段”是由JVM底层加锁进行的。当外部类Singleton 被加载时,内部静态类InnerObject 不会被加载。只有第一次调用getInstance()方法时才会加载和初始化内部静态类。

线程安全:JVM在类初始化时,会获得初始化锁,因此多个线程同时调用getInstance()方法,内部静态类也只会初始化一次。

虽然构造器是private的,但是可以通过反射强行调用构造器,因此使用反射仍可以创建第二个实例。

public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } private static class InnerObject { private static final Singleton single =new Singleton(); } public static Singleton getInstance() { return InnerObject.single; } }

内部枚举类实现(饿汉式,防止反射和序列化攻击)

枚举类enum会继承java.lang.Enum 的 final 类。枚举常量会被编译成静态成员(public static final修饰),在静态代码块中实例化,同样会获得类加载锁,保证了实例的唯一性。

Java反射会有检验,通过反射创建枚举实例时会抛出异常IllegalArgumentException。

枚举的序列化只写入常量的name,反序列化会从缓存哈希表中获取已经存在的单例,不会重新构建。

枚举类已经继承Enum,无法继承其他类。

public class SingletonFactory { private enum EnumSingleton { Singleton; private Singleton singleton; private EnumSingleton() { singleton = new Singleton(); } public Singleton getInstance() { return singleton; } } public static Singleton getInstance(){ return EnumSingleton.Singleton.getInstance(); } } public class Singleton { private Singleton() { System.out.println("构造函数Singleton"); } }
http://www.jsqmd.com/news/1094108/

相关文章:

  • 2026年企业采购AI外呼系统:怎么选性价比更高?
  • 终极免费跨平台KVM软件指南:如何用Barrier一套键鼠控制多台电脑
  • 一次 GitHub Actions 翻车实录:E2E 测试把我的后端项目按在地上做体检
  • pg空值管理
  • 深入解析无列名SQL注入:原理、实战与防御
  • Pi Agent 对接实现:消息解析、重试与取消
  • QuantConnect Lean算法交易引擎:从零开始构建专业量化交易系统的完整指南
  • 私教服务 | 他不加班,项目延期了,我该怎么办?
  • 一套 Spec-First 的 AI 编程工作流
  • 基于HAL库的STM32笔记——GPIO
  • 作为Python开发者值得关注的五个第三方库
  • ITSM系统里的工单分类:为什么分类越细,IT服务台反而越难用?
  • AI教材写作新突破!借助AI工具快速编写教材,低查重率不是梦
  • 进阶调节作用分析 | 多个自变量、二分类因变量、有序因变量及面板数据都能做
  • 自动售货机和传统便利店的区别,哪个更有优势?~YH
  • 聚龙汇刘睿 以信任为基石 打造投资社群新生态
  • PHP代码审计实战:preg_match正则绕过与无字母数字WebShell构造
  • 告别付费:Android原生TTS引擎的离线语音合成实战
  • 联想拯救者BIOS隐藏功能解锁:5分钟释放你的笔记本全部性能
  • 2026实测12款论文降AI率软件,效果最好的竟然是它!
  • Agent Runtime 范式革命:从混沌执行到确定性系统
  • 深入解析JavaScript原型链污染:原理、危害与防御实战
  • 2026年为什么越来越多家庭开始重视家庭系统建设?
  • agent 学习
  • AI期刊论文写作工具哪个好?2026年主流工具横向测评
  • 艺起玩一夏 | 暑期用小艺做攻略、听讲解、修美图,轻松玩转沉浸式研学
  • Java毕业设计-基于 SpringBoot 的戏曲文化科普与分享平台设计 传统文化视域下戏曲传播管理系统的设计与实现(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 主流办公APP对比,图文会议总结功能谁更实用
  • 2026一线大厂Java面试八股文整理(附答案)| 建议收藏
  • ByteArrayInputStream和DataInputStream的源码分析和使用方法详细分析前言)UTF-8 编码规则