单例模式对比:静态内部类 vs. 饿汉式

server/2024/9/22 20:22:44/

单例模式是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。Java 中有多种实现单例模式的方式,其中静态内部类实现和饿汉式实现是两种常见的方法。本文将对这两种单例模式进行详细对比,说明它们在延迟加载方面的区别,并解释为何某种实现方式会导致实例一上来就加载。

⚠️ 有一点需要大家先明确:JVM 对于类的加载是只有类 首次被使用 的时候才会被加载。但不同的单例模式实现方式会影响实例的加载时机。

1. 静态内部类实现单例模式
实现代码:
public class Singleton {// 私有构造函数,防止外部实例化private Singleton() {}// 静态内部类,持有 Singleton 的唯一实例private static class Holder {private static final Singleton INSTANCE = new Singleton();}// 提供对外的公共方法获取单例实例public static Singleton getInstance() {return Holder.INSTANCE;}
}
特点:
  • 延迟加载:静态内部类 Holder 只有在 getInstance() 方法被首次调用时才会被加载。JVM 确保 Holder 类在首次访问其静态成员 INSTANCE 时才会被初始化,从而实现了懒加载。这样可以避免在类加载时立即创建实例,节省资源。
  • 线程安全:静态内部类的加载是由 JVM 保证线程安全的,因此静态内部类单例模式天然地实现了线程安全。
2. 饿汉式实现单例模式
实现代码:
public class Singleton {// 立即创建单例实例private static final Singleton INSTANCE = new Singleton();// 私有构造函数,防止外部实例化private Singleton() {}// 提供对外的公共方法获取单例实例public static Singleton getInstance() {return INSTANCE;}
}
特点:
  • 饿汉式加载:在类加载时,静态成员变量 INSTANCE 被立即初始化。这意味着即使这个实例可能在类加载之后不会立即使用,它依然会在类加载时就被创建。这种方式没有延迟加载的机制,因此会在类加载时消耗资源。
  • 线程安全:由于 INSTANCE 在类加载时被初始化,类的加载过程是线程安全的,因此饿汉式单例模式天然是线程安全的。

对比分析

  1. 延迟加载
    • 静态内部类:支持延迟加载。静态内部类 Holder 只有在 getInstance() 方法首次被调用时才会被加载,这样避免了不必要的资源消耗。此处的延迟加载是因为静态内部类 Holder 的实例化是依赖于 getInstance() 方法的调用,而非类加载。
    • 饿汉式:不支持延迟加载。实例 INSTANCE 在类加载时就被创建,即使不立即使用,也会占用内存资源。这是因为在声明 INSTANCE 时就执行了 new Singleton(),导致类加载时即创建实例。
  2. 资源消耗
    • 静态内部类:只有在真正需要时才创建实例,因此更为高效,适合实例创建开销较大的情况。避免了在类加载时不必要的资源消耗。
    • 饿汉式:实例在类加载时就创建,即使没有实际使用,也会占用内存资源。这可能会导致内存消耗增加,尤其是在实例创建开销较大的情况下。
  3. 实现复杂度
    • 静态内部类:实现相对复杂,但支持延迟加载,能够有效节省资源。代码的维护和理解稍微复杂一些,但具有更好的资源管理能力。
    • 饿汉式:实现简单,但可能会导致不必要的内存消耗。其设计简单明了,但资源消耗可能会在不需要的情况下增加。
  4. 代码影响
    • 静态内部类:延迟加载是由于 Holder 类的静态成员 INSTANCE 只有在实际访问 getInstance() 方法时才会被初始化。JVM 确保 Holder 类的加载是按需进行的,这样实现了懒加载。
    • 饿汉式:实例立即加载是因为 INSTANCE 是在类加载时初始化的。private static final Singleton INSTANCE = new Singleton(); 这一行代码会在类加载时立即执行,导致实例在类加载时即创建。

总结

  • 静态内部类单例模式通过利用静态内部类的懒加载机制,实现了在实际需要时才创建实例的效果。这种方式在资源管理和延迟加载方面表现优异。
  • 饿汉式单例模式由于实例在类加载时就创建,不支持延迟加载,可能会在实例不需要时浪费资源。其实现简单,但可能导致不必要的内存消耗。

选择合适的单例模式实现方式应根据实际需求和资源情况来决定。如果需要高效的资源管理和懒加载,静态内部类实现是一个更优的选择。对于实现简单、对资源消耗不敏感的场景,饿汉式实现也是一种有效的方式。


http://www.ppmy.cn/server/114593.html

相关文章

【有啥问啥】HashHop在LTM-2-mini中的应用:解锁长期记忆模型的新纪元

HashHop在LTM-2-mini中的应用:解锁长期记忆模型的新纪元 引言 随着AI技术的飞速发展,模型在处理复杂任务和数据时所需的上下文窗口大小也在不断扩展。深度学习模型在处理超长上下文时,往往面临着计算资源消耗高、上下文丢失等问题。近期&am…

Cannot Locate Document 原理图导入pcb出现报错

将原理图update到pcb时报错Cannot Locate Document: 记得保存pcb到你的项目就可以了

JVM 的类加载机制和双亲委派机制

1.基本概念: 在Java虚拟机(JVM)中,类加载机制是其核心组成部分之一,它负责将类(.class文件)加载到JVM的方法区内,并在需要时初始化这些类。本文将深入探讨JVM的类加载机制&#xff0…

Packet Tracer - 单区域OSPFv2的配置方法以及思路

Packet Tracer - 单区域OSPFv2的配置思路 1、思路前夕查看 做这个的时候大家了解一下通配符,不然不理解这个东西为什么子网掩码为什么会取反 这里给大家简单演示一下 2、使用进程 ID 10 在所有路由器上激活 OSPF。 在 Headquarters 网络中的路由器上使用 network…

Python画笔案例-041 绘制正方形阶梯

1、绘制正方形阶梯 通过 python 的turtle 库绘制正方形阶梯,如下图: 2、实现代码 绘制正方形阶梯,以下为实现代码: """正方形阶梯.py """ import turtledef draw_square(length):for _ in range(6…

【C语言】字符串函数详细讲解

文章目录 前言求字符串长度(strlen)strlen的声明和使用strlen模拟实现 字符串拷贝(strcpy)strcpy的声明和使用strcpy模拟实现 字符串追加函数(strcat)strcat的声明和使用strcat模拟实现 字符串比较函数&…

网络基础入门指南(二)

一、什么是交换机 交换机,Switch 用于将多台计算机/交换机连接到一起,组建网络 交换机负责为其中任意两台计算机提供独享线路进行通信类型: 非网管(即插即用),便宜,不可管理 网管&#xff0…

了解Http和Https的区别

HTTP 和 HTTPS 是用于在互联网上传输数据的两种协议。它们的主要区别在于安全性、传输方式和工作机制。下面是详细的讲解: 1. HTTP (HyperText Transfer Protocol) 概述 HTTP 是超文本传输协议,用于在 Web 浏览器与服务器之间传输数据。它是一个无状态、…