小蔡学Java

Java之设计模式(一)

2023-09-10 17:56 886 0 设计模式 设计模式单例模式

单例模式(Singleton Pattern)

  单例模式是最常用的设计模式之一。它可以确保在整个应用程序中,某个类只有一个实例存在,并提供一种访问这个实例的全局访问点。单例模式在需要限制某些类的实例数量时非常有用。 它通常用于需要全局访问的资源,如配置文件、日志记录器、数据库连接等。

应用场景

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

数据库连接 在一个应用程序中,如果需要频繁地与数据库交互,使用单例模式可以确保只有一个数据库连接实例,从而减少数据库连接的数量,提高应用程序的性能。

系统配置 在一个应用程序中,通常会有一些全局的配置参数,如数据库连接字符串、服务器地址、缓存大小等。使用单例模式可以确保只有一个配置实例,从而方便管理和修改配置参数。

代码实现

懒汉式

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

静态内部类方式

  SingletonHolder是一个静态内部类,它包含一个静态的INSTANCE成员变量,用于存储单例对象。在第一次调用getInstance方法时,静态内部类会被加载,从而创建单例对象。这种方式既兼顾了线程安全又兼顾了延迟加载的需求。

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

饿汉式

  饿汉式在类加载时就创建了单例对象,所以不存在线程安全问题。不过,这种方式可能会导致不必要的资源浪费,因为单例对象的创建可能在应用程序启动时就完成了,而有些应用场景中可能并不需要使用单例对象。

public class Singleton {
    // 在类加载时就创建单例对象
    private static Singleton instance = new Singleton();
    
    // 将构造函数设为私有,禁止外部创建实例
    private Singleton() {}
    
    // 提供获取单例对象的方法
    public static Singleton getInstance() {
        return instance;
    }
}

双重检查锁

  它可以在保证线程安全的同时实现延迟加载

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;
    }
}

枚举方式

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

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // TODO: 实现单例对象的功能
    }
}

使用小结

对线程安全和性能要求较高,可以考虑使用饿汉式或双重检查锁方式实现单例模式。这两种方式都能保证线程安全,而且在大多数情况下性能也比较好。

如果你对线程安全要求不是很高,或者希望在第一次访问时才创建单例对象,可以考虑使用懒汉式或者静态内部类方式。这两种方式都是延迟加载的,只有在需要时才会创建单例对象。懒汉式不是线程安全的,需要通过加锁等方式来保证线程安全;而静态内部类方式则是天生线程安全的,不需要额外的处理。

希望实现简单、代码少,且不需要考虑线程安全和延迟加载的问题,可以考虑使用枚举方式。这种方式不仅代码简单,而且天生线程安全、单例对象创建和调用都很方便。

总之,选择哪种实现方式需要根据具体需求来决定,需要综合考虑线程安全、性能、代码复杂度、延迟加载等因素。

评论( 0 )

  • 博主 Mr Cai
  • 坐标 河南 信阳
  • 标签 Java、SpringBoot、消息中间件、Web、Code爱好者

文章目录