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

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

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

1. 静态内部类实现单例模式
实现代码:
java 复制代码
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. 饿汉式实现单例模式
实现代码:
java 复制代码
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(); 这一行代码会在类加载时立即执行,导致实例在类加载时即创建。

总结

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

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

相关推荐
C++忠实粉丝2 天前
Linux系统基础-多线程超详细讲解(5)_单例模式与线程池
linux·运维·服务器·c++·算法·单例模式·职场和发展
FANGhelloworld3 天前
C++面向对象设计模式——单例模式
c++·单例模式·设计模式
土了个豆子的3 天前
单例模式的概念和用处
单例模式
飞升不如收破烂~3 天前
在Spring框架中,容器管理的bean可以有不同的作用域(scope),其中最常用的两种是单例(singleton)和原型(prototype)。
spring·单例模式·原型模式
晨曦丿3 天前
设计模式之——单例模式
c++·单例模式·设计模式
徐子童4 天前
多线程案例---单例模式
java·单例模式·设计模式
Good_tea_h5 天前
线程安全的单例模式(Singleton)。
单例模式
Tang Paofan5 天前
C++单例模式
java·c++·单例模式
nice666606 天前
DAO模式及单例模式
java·数据库·sql·mysql·单例模式·idea
慕木沐6 天前
【创建型】单例模式
java·开发语言·单例模式