为什么有java单例类?你什么时候需要使用一个

时间:2011-02-12 16:05:33

标签: java design-patterns singleton

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

谢谢,

11 个答案:

答案 0 :(得分:14)

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

为什么要使用Singletons?

你可以通过谷歌“单身人士”找到种种原因。来自JavaWorld

  

有时候这样做是合适的   恰好是一个类的一个实例:   窗户经理,印刷品,和   文件系统是典型的例子。   通常,那些类型的   被称为单身的物体   由不同的对象访问   整个软件系统,和   因此需要一个全球性的观点   访问。当然,就在你的时候   确定你永远不会需要更多   一个例子,这是一个很好的赌注   改变主意。

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

有几个原因

  1. 您可以使用继承
  2. 您可以使用接口
  3. 这样可以更轻松地对单例类本身进行单元测试
  4. 它可以对依赖于单例的代码进行单元测试
  5. 对于#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
    }
    

    (注意使用Initialization On Demand Holder模式)

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

    但请注意,使用静态getInstance()方法可能会导致“静态绑定”,其中依赖于您的单例的代码无法进行单元测试。 静态单身通常不被认为是一种好习惯因为这一点(见this blog post

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

答案 1 :(得分:4)

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

单身是一个只有1个实例的类。

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

这与使用静态方法的实用程序类有很大区别 - 那里没有涉及的状态。如果有,则必须检查(在同步块中)是否已创建状态,然后按需初始化它(懒惰)。对于某些问题,这确实是一种解决方案,但您需要根据每次方法调用的开销来支付费用。

答案 2 :(得分:1)

数据库实例是一个单例非常有用的地方,因为一个线程只需要一个数据库连接。我打赌有很多其他的实例,比如数据库连接,你只需要一个实例,这就是你要使用单例的地方。

答案 3 :(得分:1)

使用singleton pattern封装每个应用程序只应创建(初始化)一次的资源。您通常对管理共享实体(如数据库)访问的资源执行此操作。单例可以控制有多少并发线程可以访问该共享资源。即因为有一个数据库连接池,它可以控制向需要它们的那些线程分发多​​少个数据库连接。 Logger是另一个示例,其中记录器确保可以适当地管理对共享资源(外部文件)的访问。通常单身人士也会用来加载昂贵(慢)的资源来创建。

你通常会创建一个像这样的单例,在getInstance上同步:

public class Singleton {

    private static Singleton instance;

    private Singleton(){
         // create resource here
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }

        return instance;
    }
}

但是创建它同样有效,

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton(){
        // create resource here
    }

    public static Singleton getInstance() {
        return instance;
    }
}

这两种方法都将创建一个实例PER类加载器。

答案 4 :(得分:1)

对我而言,使用静态方法优先选择单例的原因是可测试性。假设我确实需要确保一个且只有一个类的实例。我可以使用单例或只使用静态方法的静态类来实现。我们还要说我想在另一个类中使用这个类,但出于测试目的,我想嘲笑第一个类。唯一的方法是将类的实例注入第二个类,并要求您具有非静态类。在测试方面你仍然有些痛苦 - 你可能需要构建一些你可以使用反射调用的代码来删除单例以进行测试。您也可以使用接口(虽然这将明确允许使用除另一个开发人员的单例以外的东西),并简单地将单例作为接口的实例提供给使用它的类。

答案 5 :(得分:1)

一个考虑因素是将单例作为实例允许您实现接口。仅仅因为你想控制它的实例化,并不意味着你希望每一段代码知道它是一个单例。

例如,假设您有一个创建数据库连接的连接提供程序单例。

public class DBConnectionProvider implements ConnectionProvider {}

如果它是一个具有静态方法的类,则无法注入依赖项,如下所示:

public void doSomeDatabaseAction(ConnectionProvider cp) {
   cp.createConnection().execute("DROP blah;");
}

必须是

public void doSomeDatabaseAction() {
   DBConnectionProvider.createConnection().execute("DROP blah;");
}

如果您以后想要对方法进行单元测试(您可以传入模拟的连接提供程序)以及其他内容,则依赖注入非常有用。

答案 6 :(得分:0)

单例具有静态变量和方法的优势:它是一个对象实例,可以从一个类继承(例如:一个具有单个主JFrame的应用程序),并扩展一个或多个接口(从而被视为实现这些接口的任何其他对象。)

答案 7 :(得分:0)

您何时需要使用? 有许多对象我们只需要其中一个:线程池,高速缓存,对话框,处理首选项和注册表设置的对象,用于记录的对象,以及充当打印机和图形卡等设备的设备驱动程序的对象。对于许多这些类型的对象,如果我们要多个实例化,我们会运行intol各种问题,如错误的程序行为或过度使用资源

关于同步的使用,它绝对是昂贵的,并且唯一的时间同步是第一次或唯一的实例,一旦实例化,我们不再需要再次同步。在第一次完成之后,完全不需要同步化:(

答案 8 :(得分:0)

Some people think that singletons are useless and dangerous.我不完全同意,但有些观点是有效的,应该谨慎使用单身。

在我看来,用来取代单身人士的静态课程更加糟糕。静态类应主要用于对不共享公共资源的相关函数进行分组。 java.util.Collections就是一个很好的例子。它只是一堆与任何对象无关的函数。

另一方面,单身人士是真实的对象。理想情况下,单例应该在没有单例模式的情况下实现。如果您突然需要,这将允许您轻松切换到使用多个实例。例如,您可能只有一个数据库连接,但是您必须同时使用另一个完全不相关的数据库。你只需将你的单身人士变成“双人”或“三人”或其他什么。如果需要,还可以将其构造函数设置为public,这样可以根据需要创建任意数量的实例。所有这些东西都不容易用静态类实现。

简单地说,单例是一个常规类,它有一个全局可用的实例,以免您将其作为参数传递到任何地方。

创建单身人士并没有太大的麻烦。您只需使用私有构造函数创建一个常规类,然后实现一个工厂方法,然后就完成了。

答案 9 :(得分:0)

在我回复时,其他人提供了更好的答案,但我会留下我的答案给后人。看来单元测试是这里的重要动力。

出于所有意图和目的,没有理由选择单身人士来描述你所采用的方法。有人认为静态变量是大写-b Bad(就像其他有时候很有用的功能一样)因为它们离全球数据不远,作为回应我们有单身的解决方法。

答案 10 :(得分:0)

它们也与其他模式一起使用。见Can an observable class be constructed as a singleton?