天天看点

从原理到实践:面向对象设计中的经典模式-单例模式

作者:玄明Hanko

在Java中,单例模式是一种设计模式,它保证一个类只有一个实例,并提供全局访问点以便其他对象可以访问该实例。实现单例模式的关键在于确保只有一个对象被创建并且该对象不会被多次实例化,保证被创建一次,节省系统开销。

5.1、单例实现方式

  • 饿汉式
  • 懒汉式
  • 懒汉式+Synchronized
  • 双重校验
  • 静态内部类
  • 枚举(推荐方式)
从原理到实践:面向对象设计中的经典模式-单例模式

5.2、实现代码

  • 饿汉式
package com.hanko.designpattern.singleton;
/**
 * 饿汉式 (饿怕了,担心没有吃,所以在使用之前就new出来)
 *优点:实现简单,安全可靠
 *缺点:在不需要时,就已实例化了
 * @author hanko
 * @version 1.0
 * @date 2020/9/14 18:50
 */
public class HungrySingleton {
    //特点一 静态私有变量 直接初始化
    private static HungrySingleton instance = new HungrySingleton();
    //特点二 构造函数私有
    private HungrySingleton(){
    }
    public static HungrySingleton getInstance(){
        return instance;
    }
    public void doSomething(){
        //具体需要实现的功能
    }
}           
  • 懒汉式
package com.hanko.designpattern.singleton;
/**
 * 懒汉式(非常懒,所以在要使用时再去new)
 *优点:简单
 *缺点:存在线程安全问题
 * @author hanko
 * @version 1.0
 * @date 2020/9/14 18:50
 */
public class SluggardSingleton {
    //特点一 静态私有变量,先不初始化
    private static SluggardSingleton instance;
    //特点二 构造函数私有
    private SluggardSingleton(){
    }
    //特点三 null判断,没有实例化就new
    public static SluggardSingleton getInstance(){
        if(instance == null){
            instance = new SluggardSingleton();
        }
        return instance;
    }
    public void doSomething(){
        //具体需要实现的功能
    }
}           
  • 懒汉式+Synchronized
package com.hanko.designpattern.singleton;
/**
 * 懒汉式(非常懒,所以在要使用时再去new)
 *优点:简单
 *缺点:存在线程安全问题
 * @author hanko
 * @version 1.0
 * @date 2020/9/14 18:50
 */
public class SluggardSingleton {
    //特点一 静态私有变量,先不初始化
    private static SluggardSingleton instance;
    //特点二 构造函数私有
    private SluggardSingleton(){
    }
    //特点三 null判断,没有实例化就new
    public static synchronized SluggardSingleton getInstance(){
        if(instance == null){
            instance = new SluggardSingleton();
        }
        return instance;
    }
    public void doSomething(){
        //具体需要实现的功能
    }
}           
  • 双重校验
package com.hanko.designpattern.singleton;
/**
 * 双重校验
 *对懒汉式单例模式做了线程安全处理增加锁机制
 * volatile变量级
 * synchronized 类级
 * @author hanko
 * @version 1.0
 * @date 2020/9/15 9:53
 */
public class DoubleCheckSingleton {
    //特点一 静态私有变量,增加volatile变量级锁
    private static volatile DoubleCheckSingleton instance;
    //特点二 构造函数私有
    private DoubleCheckSingleton(){
    }
    //特点三 双重null判断  synchronized类级锁
    public static DoubleCheckSingleton getInstance(){
        if (instance == null){
            synchronized(DoubleCheckSingleton.class){
                if (instance == null){
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}           
  • 静态内部类
package com.hanko.designpattern.singleton;
/**
 * 内部静态类方式
 *优点:静态内部类不会在InnerStaticSingleton类加载时加载,
 * 而在调用getInstance()方法时才加载
 *缺点:存在反射攻击或者反序列化攻击
 * @author hanko
 * @version 1.0
 * @date 2020/9/15 10:03
 */
public class InnerStaticSingleton {
    //特点一:构造函数私有
    private InnerStaticSingleton(){
    }
    //特点二:静态内部类
    private static class InnerSingleton{
        private static InnerSingleton instance = new InnerSingleton();
    }
    public InnerSingleton getInstance(){
        return InnerSingleton.instance;
    }
    public void doSomething(){
        //do Something
    }
}           
  • 枚举(推荐方式)

在Java中,使用枚举实现单例模式是一种非常简洁和安全的方式。在枚举中,每个枚举常量都是该枚举类型的唯一实例。因此,只需要将单例对象定义为一个枚举类型,就可以保证它是线程安全的,并且只有一个实例。

以下是使用枚举实现单例模式的示例代码:

package com.hanko.designpattern.singleton;

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 单例对象的方法
    }
}           

在上面的代码中,Singleton是一个枚举类型,其中只定义了一个枚举常量INSTANCE。由于枚举常量在Java中是唯一的,因此INSTANCE就是Singleton的唯一实例。

使用枚举实现单例模式有以下优点:

  1. 线程安全:枚举常量在Java中确保是线程安全的,并且只会被实例化一次。
  2. 简洁易懂:相比其他实现方式,枚举实现单例模式更加简洁易懂。
  3. 防止反射攻击:由于枚举类型没有构造方法,因此无法通过反射来创建枚举常量的多个实例。

综上所述,使用枚举实现单例模式是一种安全、简单和高效的方式。

5.3、相关问题

1)什么是单例模式?

单例模式是一种设计模式,它保证一个类只有一个实例,并提供一个全局访问点以便其他对象可以访问该实例。

2)如何实现单例模式?

常见的单例模式实现方式包括饿汉式、懒汉式、双重检查锁定和静态内部类等。其中,饿汉式和静态内部类方式都是线程安全的,而懒汉式和双重检查锁定需要考虑多线程环境下的线程安全问题。

3)为什么使用单例模式?

使用单例模式可以确保在整个应用程序中只有一个实例存在,并且该实例能够被全局访问。这样可以避免创建过多的对象,节省系统资源,并且能够方便地管理全局状态。

4)单例模式有哪些优缺点?

优点:

  • 单例模式可以确保只有一个实例存在,避免了创建过多的对象浪费系统资源。
  • 可以方便地管理全局状态,消除重复的数据读取/写入等操作。

缺点:

  • 单例模式可能会增加代码的复杂度和理解难度。
  • 在多线程环境下,需要考虑线程安全问题。
  • 单例模式可能会对代码的可测试性产生影响。

5)如何保证单例模式的线程安全?

可以使用synchronized关键字、volatile关键字、静态内部类等方式来保证单例模式的线程安全。其中,饿汉式和静态内部类方式是最常见的线程安全实现方式。

5.4、相关应用

在Spring框架中,单例模式被广泛应用于Bean的管理和实例化过程中。默认情况下,Spring容器会将所有的Bean都设置为单例模式,并且在第一次被访问时进行实例化。

具体来说,Spring中单例模式的应用有以下几个方面:

1)Bean的作用域:默认情况下,Spring中的Bean都是单例的,也就是说它们只会被实例化一次,并且可以被多个对象共享。除了单例之外,Spring还支持原型、请求、会话和全局会话等多种作用域。

@Component // 默认是单例的
    public class MyBean {
        // ...
    }           

2)生命周期回调:在Bean的生命周期中,Spring提供了多个回调方法,包括初始化前、初始化后、销毁前和销毁后等。通过实现这些回调方法,可以对Bean的生命周期进行管理,从而提高应用程序的可靠性和稳定性。其中,在单例模式下,Bean的初始化和销毁只会发生一次。

@Component
    public class MyBean implements InitializingBean, DisposableBean {
        // ...

        @Override
        public void afterPropertiesSet() throws Exception {
            // Bean初始化完成后的操作
        }

        @Override
        public void destroy() throws Exception {
            // Bean销毁前的操作
        }
    }           

单例模式在Spring中被广泛应用于Bean的管理和实例化过程中,并且通过作用域、延迟初始化和生命周期回调等特性,提供了更加灵活和可靠的Bean管理方式。

==================================

如果文章对你有帮助,不要忘记加个关注、点个赞!!!

继续阅读