Java 内部类实现单例模式

引言

单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供全局访问点。在 Java 中,通常使用静态变量来实现单例模式。然而,使用内部类实现单例模式是一种更加安全且线程安全的方式。本文将介绍什么是内部类以及如何使用内部类实现单例模式。

内部类

内部类是定义在其他类内部的类。它可以访问包含它的外部类的成员,包括私有成员。内部类有以下几种类型:

  • 成员内部类:定义在类的内部,但不在任何方法内部的类。它可以访问外部类的所有成员,包括私有成员。

  • 局部内部类:定义在方法内部的类,它只能在方法内部使用。

  • 匿名内部类:没有类名的内部类,通常用于创建实现某个接口或继承某个类的匿名类。

  • 静态内部类:与成员内部类类似,但它是静态的,可以直接通过外部类访问,不需要创建外部类的实例。

在本文中,我们将使用成员内部类来实现单例模式。

单例模式

单例模式是一种创建型设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。单例模式有以下几个特点:

  • 私有构造函数:确保其他类不能直接实例化该类。

  • 静态变量:用于保存单例实例。

  • 静态方法:用于获取单例实例。

传统的单例模式实现如下:

public class Singleton {
  private static Singleton instance;
  
  private Singleton() {
    // 私有构造函数
  }
  
  public static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

然而,上述实现在多线程环境下存在线程安全问题。为了解决这个问题,我们可以使用内部类实现单例模式。

内部类实现单例模式

内部类可以延迟加载,只有在第一次使用时才会被加载。这个特性可以用来实现线程安全的单例模式。

以下是使用内部类实现单例模式的代码示例:

public class Singleton {
  private Singleton() {
    // 私有构造函数
  }
  
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  
  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

在上述代码中,我们将 Singleton 类的实例化过程放在了一个私有的静态内部类 SingletonHolder 中。由于静态内部类只会在第一次使用时才会被加载,因此可以保证单例实例的延迟加载。同时,静态内部类只会被加载一次,因此可以确保单例实例的线程安全性。

示例

下面我们通过一个示例来说明使用内部类实现单例模式的优势。

假设我们有一个日志记录器,需要记录系统中的日志信息。我们希望全局只有一个日志记录器实例,并可以通过全局访问点来获取该实例。

首先,我们定义一个简单的日志记录器类:

public class Logger {
  private List<String> logList;
  
  private Logger() {
    logList = new ArrayList<>();
  }
  
  public void addLog(String log) {
    logList.add(log);
  }
  
  public void printLogs() {
    for (String log : logList) {
      System.out.println(log);
    }
  }
}

传统的单例模式实现如下:

public class Singleton {
  private static Singleton instance;
  private Logger logger;
  
  private Singleton() {
    logger = new Logger();
  }
  
  public static Singleton getInstance() {
    if (instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
  
  public Logger getLogger() {
    return logger;
  }
}

上述实