设计模式 - 单例模式(懒汉式、饿汉式、静态内部类、枚举)

目录

一、单例模式的核心概念

二、实现单例模式的方法

2.1 懒汉式(Lazy Initialization)

2.1.1 线程不安全版本

2.1.2 双重检查锁定(线程安全)

2.2 饿汉式(Eager Initialization)

2.3 静态内部类(Static Inner Class)

2.4 枚举(Enum)

三、单例模式的优缺点

优点

扫描二维码关注公众号,回复: 17418009 查看本文章

缺点

四、总结


设计模式是在软件设计中为解决特定问题而反复出现的解决方案的描述。它们不是完成任务的具体代码,而是模板或蓝图,指导开发者如何有效地解决问题。设计模式通常涉及类的行为和交互方式,它们可以帮助开发者编写可维护、可扩展和可复用的代码。

单例模式是一种常用的软件设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这种模式在很多场景下都非常有用,特别是在需要对资源进行集中管理和控制的情况下。

一、单例模式的核心概念

单例模式的核心在于两个方面:

  1. 确保类只有一个实例:通过某种机制来防止多次实例化同一个类。
  2. 提供一个全局访问点:使得应用程序中的任何部分都可以访问到这个唯一的实例。

二、实现单例模式的方法

2.1 懒汉式(Lazy Initialization)

懒汉式单例模式会在第一次调用getInstance方法时才创建实例。

2.1.1 线程不安全版本

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

2.1.2 双重检查锁定(线程安全)

为了使其线程安全,可以采用同步方法或者双重检查锁定(Double Checked Locking):

public class LazySingletonThreadSafe {
    // 使用volatile关键字保证可见性和有序性
    private volatile static LazySingletonThreadSafe uniqueInstance;  

    private LazySingletonThreadSafe() {
        // 防止反射攻击
        if (uniqueInstance != null) {
            throw new IllegalStateException("Singleton instance already created!");
        }
    }

    public static LazySingletonThreadSafe getInstance() {
        if (uniqueInstance == null) { // 第一次检查
            synchronized(LazySingletonThreadSafe.class) {
                if (uniqueInstance == null) { // 第二次检查
                    uniqueInstance = new LazySingletonThreadSafe();
                }
            }
        }
        return uniqueInstance;
    }
}

2.2 饿汉式(Eager Initialization)

饿汉式单例模式在类加载的时候就创建了实例,因此没有线程安全问题。

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return instance;
    }
}

2.3 静态内部类(Static Inner Class)

这种方式结合了懒汉式和饿汉式的优点,实现了线程安全并且只有在第一次调用getInstance方法时才会初始化。

public class StaticInnerSingleton {
    private StaticInnerSingleton() {}

    private static class SingletonHolder {
        private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
    }

    public static StaticInnerSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

2.4 枚举(Enum)

枚举可以用来实现单例模式,这是《Effective Java》推荐的方式之一,简洁且线程安全。

public enum EnumSingleton {
    INSTANCE;

    public void someMethod() {
        // 实现代码
    }
}

三、单例模式的优缺点

优点

  1. 节省资源:因为单例模式只允许一个实例存在,所以在内存使用上比较经济。
  2. 简化访问:提供全局访问点,方便在任何地方获取实例。
  3. 易于扩展:可以很容易地扩展原有的功能而不影响其他模块。

缺点

  1. 难以测试:单例模式破坏了对象的封闭性,使得在单元测试时很难模拟单例的行为。
  2. 违反单一职责原则:单例类通常承担了过多的责任。
  3. 潜在的并发问题:在多线程环境下需要特别注意线程安全问题。

四、总结

单例模式是一种非常实用的设计模式,尤其适用于那些需要频繁访问且创建成本较高的对象。

然而,在使用单例模式时也需要谨慎,因为它可能会引入一些设计上的问题,特别是当单例类变得过于庞大时。

在现代软件架构中,依赖注入(DI)框架(如Spring)的使用越来越广泛,它提供了一种更加灵活的方式来管理对象的生命周期,因此在很多情况下,单例模式可以通过依赖注入来实现,而不是手动实现。

因为:Spring框架本身就提供了依赖注入(Dependency Injection, DI)机制来管理bean的生命周期,并且默认情况下bean是以单例(Singleton)模式存在的。这意味着,对于大多数场景来说,你只需要声明一个类,并通过适当的注解(如@Service@Repository@Controller等)标记它,Spring容器就会为你管理这个bean的实例化和生命周期。对于同一个bean类型,Spring容器只会创建一个实例,并且每次请求该bean时都会返回相同的实例。

猜你喜欢

转载自blog.csdn.net/YuanFudao/article/details/143368750