详解23种设计模式——单例模式

发布于:2024-04-27 ⋅ 阅读:(32) ⋅ 点赞:(0)

单例模式 | CoderMast编程桅杆单例模式 单例模式是最常用的设计模式之一,他可以保证在整个应用中,某个类只存在一个实例化对象,即全局使用到该类的只有一个对象,这种模式在需要限制某些类的实例数量时非常有用,通常全局只需要一个该对象即可,如一些配置文件映射对象、数据库连接对象等。 饿汉模式 和 懒汉模式 单例模式根据其实现方式不同,又可以分成两种类型:饿汉模式 和 懒汉模式。 饿汉模式:...icon-default.png?t=N7T8https://www.codermast.com/dev-idea/design-patterns/create-patterns/singleton-pattern.html

单例模式是最常用的设计模式之一,他可以保证在整个应用中,某个类只存在一个实例化对象,即全局使用到该类的只有一个对象,这种模式在需要限制某些类的实例数量时非常有用,通常全局只需要一个该对象即可,如一些配置文件映射对象、数据库连接对象等。

饿汉模式 和 懒汉模式

单例模式根据其实现方式不同,又可以分成两种类型:饿汉模式 和 懒汉模式。

  • 饿汉模式:可以理解为餐馆老板认为每个顾客来到餐馆时都是快要饿死的状态,一旦请求食物,则必须立刻提供给他,必须尽快让他吃到东西,为了达到这样的效果,就必须提前把食物准备好,只等顾客来吃即可。

  • 懒汉模式:可以理解为餐馆老板是一个非常懒的人,没有客人的时候,他什么也不会准备,只有当顾客点餐以后,老板才会开始工作。

设计思想

  1. 日志记录

在应用程序中,通常会有多个模块需要记录日志,为了避免创建多个日志记录器实例,使用单例模式就可以确保只有一个日志记录器实例,从而避免重复记录日志并提高应用程序的性能。

  1. 数据库连接

在应用程序中,需要频繁的和数据库进行交互,使用单例模式可以确保只有一个数据库连接实例,从而减少数据库连接的数量,提高程序的性能,减少资源的消耗。

  1. 配置文件

在应用程序中,通常会有一些全局的配置参数,如数据连接信息、项目配置信息、缓存大小等一些参数,使用单例模式可以确保只有一个配置实例,保证了参数的唯一性,从而方便管理和修改配置参数。

实现案例

饿汉模式

这种模式下,即使当前没有人来请求资源,在类加载的时候就会提前将所需要的资源准备好,只等来拿。

因为是在类加载的阶段初始化的,且类加载在程序的执行过程中仅出现一次,故其是线程安全的。

class SingletonHungry {
    // static 修饰的,在类加载时就会被初始化
    private static final SingletonHungry singletonHungry = new SingletonHungry();

    // 将构造器设为私有的,禁止从外部创建实例
    private SingletonHungry() {

    }

    // 提供获取单例对象的方法
    public static SingletonHungry getInstance() {
        return singletonHungry;
    }
}

懒汉模式

这种模式下,只有当有人来获取资源的时候,才会开始准备资源。因为是需要进行手动初始化,在并发环境下就有可能出现构建了多个实例对象的情况,需要我们手动去处理。

  • 懒汉模式:不考虑并发

class SingletonLazy {
    // 默认不初始化
    private static SingletonLazy singletonLazy;

    // 将构造器设为私有的,禁止从外部创建实例
    private SingletonLazy() {

    }

    // 提供获取单例对象的方法
    public static SingletonLazy getInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazy();
        }

        return singletonLazy;
    }
}
  • 懒汉模式:考虑并发

使用双重检查锁它可以在保证线程安全的同时实现延迟加载

class SingletonLazy {
    // 默认不初始化
    private static SingletonLazy singletonLazy;

    // 将构造器设为私有的,禁止从外部创建实例
    private SingletonLazy() {

    }

    // 提供获取单例对象的方法
    public static SingletonLazy getInstance() {
        if (singletonLazy == null) {
            synchronized ( SingletonLazy.class ) {
                if (singletonLazy == null) {
                    singletonLazy = new SingletonLazy();
                }
            }
        }

        return singletonLazy;
    }
}

枚举方式

警告

前面的两种方式中,看似是实现了单例模式,但是 Java 中一些机制可以破坏单例模式,会导致单例对象不唯一,例如反射机制、克隆机制、序列化和反序列化等。那这该如何解决呢?答案是使用 Enum 类来实现。

使用枚举实现单例模式的好处是,可以避免反射和序列化攻击。因为枚举类型的构造函数是私有的,所以无法使用反射来创建实例;而且枚举类型的实例在序列化和反序列化时会自动处理好,所以也无法通过序列化和反序列化来破坏单例。

  • 定义枚举类

public enum SingletonEnum {
    SINGLETONENUM;

    public void doSomething() {
        // TODO:这里可以写一些单例对象的相关行为和操作
    }
}
  • 使用枚举类

public class SingletonEnumTest {
    public static void main(String[] args) {
        SingletonEnum singletonEnum = SingletonEnum.SINGLETON_ENUM;
        SingletonEnum singletonEnum2 = SingletonEnum.SINGLETON_ENUM;

        System.out.println(singletonEnum  == singletonEnum2);
    }
}

输出的结果为 true ,说明这两个是同一个对象。