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

【Java设计模式 | 创建者模式】单例模式

前言:

创建者模式主要关注是“怎么创建对象?”,它的主要特点是将对象的创建和使用分离。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。

创建模式分为:

单例模式(Singleton):【Java设计模式 | 创建者模式】单例模式-CSDN博客

工厂方法模式(Factory Method):【Java设计模式 | 创建者模式】工厂方法模式-CSDN博客

抽象工厂模式(Abstract Factory):【Java设计模式 | 创建者模式】 抽象工厂模式-CSDN博客

建造者模式(Builder)

原型模式(Prototype)

单例设计模式:

单例模式(Singleton)是Java中最简单的设计模式之一。它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有一个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式结构:

单例模式主要有以下角色:

单例类:只能创建一个实例的类。

访问类:使用单例类。

单例模式的实现:

单例模式主要分为:

饿汉式:类加载就会导致该单实例对象被创建,避免了线程同步问题。

懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建。

一、饿汉式实现:

优点:实现简单,线程安全

缺点:可能造成资源浪费(如果实例未被使用)

1.静态变量方式:

私有构造函数确保外部无法通过new创建实例。

通过静态方法getInstance()提供全局唯一访问点。

public class Singleton { // 在类加载时创建实例 private static final Singleton instance = new Singleton(); // 私有构造函数防止外部实例化 private Singleton() {} // 提供全局访问点 public static Singleton getInstance() { return instance; } }

2.静态代码块:这种实现方式与直接静态变量方式的本质相同。静态代码块方式适合需要更复杂初始化逻辑的场景。

私有构造函数防止外部实例化

全局唯一访问点通过getInstance()方法提供

public class Singleton { // 静态实例变量 private static Singleton instance; // 静态代码块初始化实例 static { instance = new Singleton(); } // 私有构造函数防止外部实例化 private Singleton() {} // 提供全局访问点 public static Singleton getInstance() { return instance; } }

二、懒汉式实现:

懒汉式单例模式的特点是延迟初始化,即在第一次调用时才创建实例。(需要判断instance是否为null)

3.线程不安全:如果有多个线程,可能存在创建多个实例的情况,导致线程不安全。

public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

4.线程安全:可以通过添加synchronized关键字实现线程安全。

public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

5.双重检查锁:直接同步整个方法会影响性能,可以使用双重检查锁定来减少同步范围。

注意volatile关键字的使用,它防止指令重排序导致的初始化问题(例如多线程情况下,可能出现空指针问题)。设计到并发编程就不过多讲解。

public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }

6.静态内部类(推荐):由于JVM在加载外部类的过程中,不会加载静态内部类,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证之后被实例化一次,并且严格保证实例化顺序线程安全由JVM保证

相对于多重检查锁实现单例模式,静态内部类的方式在保证了线程安全的前提下,实现更加的简单。

public class Singleton { private Singleton() { // 私有构造函数,防止外部实例化 } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }

7.枚举方式:枚举类是单例模式实现极力推荐的方式。属于饿汉式方式,在不考虑内存空间的情况下,首先考虑枚举方式

优点:

  • 这是实现单例模式最简洁安全的方式
  • 天生支持序列化机制
  • 防止反射攻击
  • 线程安全由JVM保证
public enum Singleton { INSTANCE; public void doSomething() { // 业务方法 } }

单例模式的破坏:

使上面定义的单例类(Singleton)可以创建多个对象(枚举方式除外)。有两种方式:序列化和反射。

序列化和反序列化:通过序列化和反序列化可以破坏单例模式,关键在于反序列化时会创建新的对象实例。

import java.io.*; // 可被序列化破坏的单例类 class Singleton implements Serializable { private static final long serialVersionUID = 1L; private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } public class BreakSingleton { public static void main(String[] args) throws Exception { Singleton instance1 = Singleton.getInstance(); // 序列化到文件 ObjectOutput out = new ObjectOutputStream(new FileOutputStream("singleton.ser")); out.writeObject(instance1); out.close(); // 反序列化 ObjectInput in = new ObjectInputStream(new FileInputStream("singleton.ser")); Singleton instance2 = (Singleton) in.readObject(); in.close(); System.out.println("instance1 hashCode: " + instance1.hashCode()); System.out.println("instance2 hashCode: " + instance2.hashCode()); System.out.println("Is same instance? " + (instance1 == instance2)); } }

防止序列化和反序列化破坏的方法:实现readResolve()方法是防止序列化破坏单例的关键。当JVM反序列化对象时,会检查类是否定义了此方法。如果存在,JVM会自动调用该方法获取对象,而不是新建实例。

import java.io.*; // 防止序列化破坏的单例类 class SafeSingleton implements Serializable { private static final long serialVersionUID = 1L; private static SafeSingleton instance = new SafeSingleton(); private SafeSingleton() {} public static SafeSingleton getInstance() { return instance; } // 关键方法:反序列化时返回已有实例 protected Object readResolve() { return instance; } } public class SafeSingletonDemo { public static void main(String[] args) throws Exception { SafeSingleton instance1 = SafeSingleton.getInstance(); // 序列化 ObjectOutput out = new ObjectOutputStream(new FileOutputStream("safe.ser")); out.writeObject(instance1); out.close(); // 反序列化 ObjectInput in = new ObjectInputStream(new FileInputStream("safe.ser")); SafeSingleton instance2 = (SafeSingleton) in.readObject(); in.close(); System.out.println("instance1 hashCode: " + instance1.hashCode()); System.out.println("instance2 hashCode: " + instance2.hashCode()); System.out.println("Is same instance? " + (instance1 == instance2)); } }

反射破坏:单例模式通常通过私有构造函数和静态方法确保只有一个实例。反射可以绕过这些限制,直接调用私有构造函数创建新实例

import java.lang.reflect.Constructor; public class BreakSingleton { public static void main(String[] args) throws Exception { Singleton instance1 = Singleton.getInstance(); // 获取私有构造函数 Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); // 突破私有访问限制 // 创建第二个实例 Singleton instance2 = constructor.newInstance(); System.out.println("Instance 1 hash: " + instance1.hashCode()); System.out.println("Instance 2 hash: " + instance2.hashCode()); System.out.println("Are instances equal? " + (instance1 == instance2)); } }

防止反射破坏的方法:

方法一:在单例类的私有构造函数中添加实例存在性检查。简单有效,但懒汉式多线程下仍有有漏洞。

public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { // 防止反射破坏单例的可选检查 if (instance != null) { throw new RuntimeException("Attempt to create duplicate singleton instance"); } } public static Singleton getInstance() { return instance; } }

方法二:通过双重检查锁定(DCL)实现线程安全的懒汉式单例,并增加反射防护机制

volatile关键字:确保多线程环境下instance变量的可见性,防止指令重排序导致的未初始化对象被引用。

私有构造函数防护:在构造函数中检查实例是否已存在,若通过反射调用构造函数会直接抛出异常。

双重检查锁定:外层判断避免每次获取实例都加锁,内层判断确保多线程环境下只创建一个实例。

public class Singleton { private static volatile Singleton instance; private Singleton() { if (instance != null) { throw new IllegalStateException("Singleton instance already exists"); } } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
http://www.jsqmd.com/news/605425/

相关文章:

  • 在Ubuntu虚拟机上玩转QNX 8.0:手把手教你搭建嵌入式开发环境
  • 飞书机器人进阶:OpenClaw接入Kimi-VL-A3B-Thinking处理群聊图片
  • 从音频到全身动捕:手把手教你用AudCast和扩散模型生成会说话、会做手势的虚拟人视频
  • 告别Matlab!用FPGA手把手实现Canny边缘检测(附Verilog代码与仿真)
  • 在Ubuntu 20.04上从源码编译CasADi C++库,顺便搞定Ipopt和HSL依赖(保姆级避坑指南)
  • 保姆级教程!小程序开发只需3步,Gemini设计 + Trae开发 + 微信开发者工具预览上架
  • [具身智能-253]:从梯度下降到成长型思维:AI深度学习与人类进化的共同算法
  • 使用私有部署的 AI 接入智能体
  • OpenClaw多模型切换:Qwen3-32B-Chat与本地小模型协同工作方案
  • html怎么用netlify部署_Netlify如何托管纯HTML静态网站
  • 从Level6到Level13:手把手带你绕过RCE靶场中的那些“奇葩”WAF
  • 开源工具WorkshopDL:突破平台限制的Steam创意工坊资源获取方案
  • 飞书机器人改造计划:OpenClaw+百川2-13B-4bits智能问答实战
  • 网站SEO查询工具可以分析什么
  • 软件供应链安全新防线:Gitee CodePecker SCA如何重塑企业级SCA工具标准
  • 2026年4月人字骨架塑料模板厂家名录:组合钢模板/钢模板厂家/钢模板生产厂家/防撞墙塑料模板/防撞护栏钢模板/选择指南 - 优质品牌商家
  • 避开这3个坑!用LangChain处理中文PDF时的编码与分页优化方案
  • 深度学习+交通领域投稿指南:我是如何用3个月完成大修并最终被IEEE T-ITS接收的
  • 从Hugging Face到阿里ModelScope:手把手教你用Transformers库在PyTorch和TensorFlow间无缝切换
  • Pixel Couplet Gen惊艳案例:游戏公司用Pixel Couplet Gen做乙巳年IP联动
  • 零代码自动化:用gemma-3-12b-it为OpenClaw添加Excel处理技能
  • IM920无线模块嵌入式驱动开发与工业通信实践
  • Golang怎么用unsafe获取结构体大小_Golang如何用Sizeof查看类型占用的字节数【方法】
  • OpenClaw性能优化指南:Phi-3-vision-128k-instruct长文本处理加速方案
  • Java注解的底层原理
  • 8.构建可维护的RAG系统:代码分层与模块化设计
  • React 组件和 Hook 必须是幂等的
  • seo优化软件入门知识_seo优化软件如何配置
  • OpenClaw:2026年最火个人AI助手,让AI真正帮你干活!
  • macOS下OpenClaw安装全攻略:百川2-13B-4bits量化版对接