为什么有java单例类?何时需要使用

2022-09-01 16:10:49

我知道单例类是一个只能有一个实例化的类,但我不明白为什么这会有用。为什么不直接创建一个包含静态变量和方法的类,并在需要时使用 synce 来确保没有两个线程同时执行该类中的方法。我只是不明白为什么有人会经历创建这种类的麻烦。我知道我在这里错过了一些东西。

谢谢


答案 1

虽然我同意其他答案,但OP在问为什么不有一个包含所有静态方法(可能具有静态字段)的类,而不是一个具有一个实例的单例。

为什么使用单例?

你可以谷歌“单例”找到各种各样的原因。来自 JavaWorld

有时,一个类的一个实例是合适的:窗口管理器、打印后台处理程序和文件系统是原型示例。通常,这些类型的对象(称为单例)由整个软件系统中的不同对象访问,因此需要全局访问点。当然,只要您确定自己永远不需要多个实例,那么打赌您会改变主意。

为什么使用单例而不是具有所有静态方法的类?

几个原因

  1. 您可以使用继承
  2. 您可以使用接口
  3. 它使对单例类本身进行单元测试变得更加容易
  4. 它可以对依赖于单例的代码进行单元测试

对于 #3,如果您的 Singleton 是数据库连接池,则要确保应用程序只有一个实例,但要对数据库连接池本身进行单元测试,而不会命中数据库(可能通过使用包范围的构造函数或静态创建方法):

public class DatabaseConnectionPool {
  private static class SingletonHolder {
    public static DatabaseConnectionPool instance = new DatabaseConnectionPool(
        new MySqlStatementSupplier());
  }

  private final Supplier<Statement> statementSupplier;

  private DatabaseConnectionPool(Supplier<Statement> statementSupplier) {
    this.statementSupplier = statementSupplier;
  }

  /* Visibile for testing */
  static DatabaseConnectionPool createInstanceForTest(Supplier<Statement> s) {
    return new DatabaseConnectionPool(s);
  }

  public static DatabaseConnectionPool getInstance() {
    return SingletonHolder.instance;
  }

  // more code here
}

(请注意使用按需初始化持有者模式)

然后,可以使用包范围方法对 进行测试。DatabaseConnectionPoolcreateInstanceForTest

但请注意,使用静态方法可能会导致“静态粘附”,其中依赖于单例的代码无法进行单元测试。因此,静态单例通常不被认为是一种好的做法(请参阅此博客文章getInstance())

相反,您可以使用Spring或Guice等依赖关系注入框架来确保您的类在生产中只有一个实例,同时仍然允许使用该类的代码是可测试的。由于 Singleton 中的方法不是静态的,因此您可以使用像 JMock 这样的模拟框架在测试中模拟您的 Singleton。


答案 2

仅具有静态方法(和私有构造函数)的类是根本没有实例(0 个实例)的变体。

单例是正好有 1 个实例的类。

这些是不同的东西,有不同的用例。最重要的是状态。单例通常保护对逻辑上只有一个的东西的访问。例如,应用程序中的 -the- 屏幕可能由单例表示。创建单例时,将初始化与此单一事物的资源和连接。

这与具有静态方法的实用程序类有很大的不同 - 那里不涉及任何状态。如果有,则必须检查(在同步块中)是否已创建状态,然后根据需要(懒惰地)对其进行初始化。对于某些问题,这确实是一种解决方案,但是您需要为每个方法调用的开销付费。


推荐