什么是 Java 中的单例模式?如何实现?

单例模式是一种常用的软件设计模式,在这种模式中,某个类的实例化次数被限制为一个。意味着,在整个应用程序中,一个类只有一个实例存在。这用于那些我们只需要一个实例来控制操作的情况下,比如:日志、驱动对象、数据库操作等。

首先,让我们来看看一个最简单的单例模式的实现:

复制代码
public class Singleton {
  private static Singleton instance;
  public static synchronized Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
  private Singleton() {}
}

在这个代码中,我们有一个Singleton类,它有一个Singleton类型的私有静态成员变量instance,同时有一个公有的静态方法getInstance。这个getInstance方法是获取Singleton实例的唯一途径。当我们第一次调用getInstance方法时,会创建一个Singleton的实例,并赋值给instance;而在后续的调用中,如果instance已经被创建,则直接返回。也就是说,Singleton类的实例最多只会被创建一次。

把构造函数设为私有的是非常关键的,这是为了防止在外部被new出新的实例。并且我们注意到,instance的创建过程被synchronized修饰,这是为了防止在多线程环境下生成多个实例。然后,是另外一种更为推荐的单例模式的实现方法,那就是使用Java的内部类来保证单例:

复制代码
public class Singleton {
    private Singleton() {}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

在这个实现中,SingletonHolder是一个私有的静态内部类,当第一次加载Singleton类时不会初始化这个静态内部类,只有在第一次调用Singleton的getInstance方法时才会导致SingletonHolder被初始化。由于实例的建立是在类加载的时候完成,这样就可以确保线程安全。这个模式的优点是既保证了线程安全,又能做到延迟加载。

除了前面提到的最基础的单例模式,有很多种使用单例模式的方式,比如"懒汉式","饿汉式","双检锁/双重校验锁 (DCL,即 double-checked locking)","登记式/静态内部类","枚举"等。其中,"饿汉式"和"懒汉式"是最常用的两种形式。

"饿汉式"是最常见的一种实现方式,这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快,它是一种典型的以时间换空间的方式。

复制代码
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton (){}
    public static Singleton getInstance() {
        return instance;
    }
}

相对应的,"懒汉式"是另一种方式,较之前示例不同,懒汉式是典型的空间换时间的方式,只有在使用的时候才去创建对象,可以节约内存。

复制代码
public class Singleton {
    private volatile static Singleton singleton ;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

然后,还有一种方式是双检锁/双重校验锁(DCL,即 double-checked locking)。这种方式在加锁的情况下,我们再检查一次,如果单例仍然不是空,就返回实例,否则就创建一个新的实例。这是一种比较复杂的实现方式,但可以在保持线程安全的前提下,提高了单例的获取性能。

复制代码
public class Singleton {
    private volatile static Singleton instance;
    private Singleton (){}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在上述的代码中,第一次检查是为了不必要的同步,第二次检查则是再同步情况下进行,保证了只生成一个实例。

理解了Java中单例模式的原理和实现后,你就可以在自己的代码中灵活应用这种设计模式了。例如在应用日志、数据库连接池等需要全局唯一的资源的管理时就可以使用到。这也是你作为Java开发者必会的面试题之一。

相关推荐
float_六七2 小时前
IntelliJ IDEA双击Ctrl的妙用
java·ide·intellij-idea
能摆一天是一天4 小时前
JAVA stream().flatMap()
java·windows
CodeCraft Studio4 小时前
PDF处理控件Aspose.PDF教程:使用 Python 将 PDF 转换为 Base64
开发语言·python·pdf·base64·aspose·aspose.pdf
零点零一4 小时前
VS+QT的编程开发工作:关于QT VS tools的使用 qt的官方帮助
开发语言·qt
颜如玉4 小时前
🤲🏻🤲🏻🤲🏻临时重定向一定要能重定向🤲🏻🤲🏻🤲🏻
java·http·源码
程序员的世界你不懂6 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
星空寻流年6 小时前
设计模式第一章(建造者模式)
java·设计模式·建造者模式
lingchen19066 小时前
MATLAB的数值计算(三)曲线拟合与插值
开发语言·matlab
gb42152877 小时前
java中将租户ID包装为JSQLParser的StringValue表达式对象,JSQLParser指的是?
java·开发语言·python
一朵梨花压海棠go7 小时前
html+js实现表格本地筛选
开发语言·javascript·html·ecmascript