什么是 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开发者必会的面试题之一。

相关推荐
骄马之死1 小时前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
z落落2 小时前
C# 泛型方法(原理、类型推断、多泛型参数)+泛型效率(普通类型 VS Object装箱 VS 泛型)
开发语言·c#
L_09072 小时前
【C++】异常
开发语言·c++
世辰辰辰2 小时前
批量修改图片/文本名子
开发语言·python·批量修改文件名
郑洁文3 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
螺丝钉code3 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
z落落4 小时前
C# 四种特殊类:抽象类、密封类、静态类、部分类
开发语言·c#
摇滚侠5 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea
VidDown5 小时前
Webhook 调试器:让第三方回调“原形毕露”
java·开发语言·javascript·编辑器·postman
折哥的程序人生 · 物流技术专研5 小时前
Java 23 种设计模式:从踩坑到精通 | 原型模式 —— 克隆对象,深拷贝与浅拷贝的坑你踩过吗?
java·设计模式·架构·原型模式·单一职责原则