简体   繁体   English

为什么会有Java单例类? 您何时需要使用一个

[英]why are there java singleton classes? When would you need to use one

I understand that a singleton class is one where there can be only one instantiation, but I don't understand why this would be useful. 我知道单例类是只能有一个实例化的类,但是我不明白为什么这样做会有用。 Why won't you just create a class with static variables and methods and use synchronize if needed to make sure that no two threads were executing a method in the class simultaneously. 为什么不创建一个带有静态变量和方法的类,并在需要时使用sync来确保没有两个线程同时在该类中执行一个方法。 I just don't get why anyone would go through the trouble of creating this kind of class. 我只是不明白为什么有人会遇到创建此类课程的麻烦。 I know I'm missing something here. 我知道我在这里想念什么。

Thanks, 谢谢,

While I agree with the other answers, the OP was asking why not have a class with all static methods (possibly with static fields) instead of a singleton where you have one instance. 当我同意其他答案时,OP询问为什么不使用具有所有静态方法的类(可能具有静态字段),而不是拥有一个实例的单例。

Why use Singletons? 为什么使用单身人士?

You can Google "singleton" to find all sorts of reasons. 您可以通过Google“单身人士”查找各种原因。 From JavaWorld : JavaWorld

Sometimes it's appropriate to have exactly one instance of a class: window managers, print spoolers, and filesystems are prototypical examples. 有时,只具有一个类的实例是适当的:窗口管理器,打印后台处理程序和文件系统是典型的示例。 Typically, those types of objects—known as singletons—are accessed by disparate objects throughout a software system, and therefore require a global point of access. 通常,那些类型的对象(称为单例对象)由整个软件系统中的不同对象访问,因此需要全局访问点。 Of course, just when you're certain you will never need more than one instance, it's a good bet you'll change your mind. 当然,当您确定不再需要多个实例时,这是一个很好的选择,您会改变主意。

Why use a Singleton instead of a class with all static methods? 为什么要使用Singleton而不是使用所有静态方法的类?

A few reasons 几个原因

  1. You could use inheritance 您可以使用继承
  2. You can use interfaces 您可以使用界面
  3. It makes it easier to do unit testing of the singleton class itself 它使对单例类本身的单元测试更容易
  4. It makes it possible to do unit testing of code that depends on the singleton 这样就可以对依赖于单例的代码进行单元测试

For #3, if your Singleton was a database connection pool, you want to insure that your application has only one instance, but do unit testing of the database connection pool itself without hitting the database (possibly by using a package-scope constructor or static creational method): 对于#3,如果您的Singleton是一个数据库连接池,则要确保您的应用程序只有一个实例,但是要对数据库连接池本身进行单元测试,而不会影响数据库(可能使用package-scope构造函数或static创建方法):

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
}

(notice the use of the Initialization On Demand Holder pattern) (注意使用Initialization on Demand Holder模式)

You can then do testing of the DatabaseConnectionPool by using the package-scope createInstanceForTest method. 然后,您可以使用package-scope的createInstanceForTest方法对DatabaseConnectionPool进行测试。

Note, however, that having a static getInstance() method can cause "static cling", where code that depends on your singleton cannot be unit tested. 但是请注意,拥有静态的getInstance()方法会导致“静态粘连”,其中依赖于单例的代码无法进行单元测试。 Static singletons are often not considered a good practice because of this (see this blog post ) 因此,通常不认为静态单例是一个好习惯 (请参阅此博客文章

Instead, you could use a dependency injection framework like Spring or Guice to insure that your class has only one instance in production, while still allowing code that uses the class to be testable. 相反,您可以使用诸如Spring或Guice的依赖项注入框架来确保您的类在生产中只有一个实例,同时仍允许使用该类的代码可测试。 Since the methods in the Singleton aren't static, you could use a mocking framework like JMock to mock your singleton in tests. 由于Singleton中的方法不是静态的,因此您可以使用JMock之类的模拟框架在测试中模拟您的Singleton。

A class with only static methods (and a private contructor) is a variant where there is no instance at all (0 instances). 仅具有静态方法(和私有构造函数)的类是一个变体,其中根本没有实例(0个实例)。

A singleton is a class for which there is exactly 1 instance. 单例是一个恰好有1个实例的类。

Those are different things and have different use cases. 这些是不同的事情,并且具有不同的用例。 The most important thing is state . 最重要的是状态 A singleton typically guards access to something of which there is logically only ever one. 通常,单例保护访问某些在逻辑上只有一个的访问。 For instance, -the- screen in an application might be represented by a singleton. 例如,应用程序中的屏幕可能由单例表示。 When the singleton is created, resources and connections to this one thing are initialized. 创建单例时,将初始化与此一件事的资源和连接。

This is a big difference with a utility class with static methods - there is no state involved there. 与带有静态方法的实用程序类相比,这是一个很大的区别-那里没有涉及状态。 If there was, you would have to check (in a synchronized block) if the state was already created and then initialize it on demand (lazily). 如果存在,则必须检查(在同步块中)状态是否已经创建,然后根据需要(延迟)初始化状态。 For some problems this is indeed a solution, but you pay for it in terms of overhead for each method call. 对于某些问题,这确实是一种解决方案,但是您需要为每个方法调用的开销付费。

Database instances is one place singletons are useful, since a thread only wants one DB connection. 数据库实例是一处有用的地方,因为线程只需要一个数据库连接。 I bet there are a lot of other instances like database connections where you only want one instance of something and this is where you would use a singleton. 我敢打赌,还有很多其他实例,例如数据库连接,在这些实例中,您只需要某个实例的一个实例,而这正是您使用单例的地方。

Use the singleton pattern to encapsulate a resource that should only ever be created (initialised) once per application. 使用单例模式封装每个应用程序只能创建(初始化)一次的资源。 You usually do this for resources that manage access to a shared entity, such as a database. 通常,此操作用于管理对共享实体(例如数据库)的访问的资源。 A singleton can control how many concurrent threads can access that shared resource. 单例可以控制有多少个并发线程可以访问该共享资源。 ie because there is a single database connection pool it can control how many database connections are handed out to those threads that want them. 即,因为只有一个数据库连接池,所以它可以控制将多少数据库连接传递给需要它们的线程。 A Logger is another example, whereby the logger ensures that access to the shared resource (an external file) can be managed appropriately. 记录器是另一个示例,记录器可确保可以适当地管理对共享资源(外部文件)的访问。 Oftentimes singletons are also used to load resources that are expensive (slow) to create. 通常,单例还用于加载创建成本高(慢)的资源。

You typically create a singleton like so, synchronising on getInstance: 通常,您像这样创建一个单例,并在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;
    }
}

But it is equally valid to create it like so, 但是像这样创建它同样有效,

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton(){
        // create resource here
    }

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

Both methods will create a single instance PER classloader. 这两种方法都将创建一个单实例PER类加载器。

For me the reason to prefer singleton over a class with static methods is testability. 对我而言,与具有静态方法的类相比,更喜欢单例的原因是可测试性。 Let's say that I actually need to ensure that there really is one and only one instance of a class. 假设我实际上需要确保一个类中确实只有一个实例。 I could do that with either a singleton or a static class with only static methods. 我可以使用单例或仅具有静态方法的静态类来实现。 Let's also say that I'd like to use this class in another class, but for testing purposes I'd like to mock the first class out. 我们还想说我想在另一个类中使用这个类,但是出于测试目的,我想模拟第一个类。 The only way to do that is to inject an instance of the class into the second class and that requires that you have a non-static class. 唯一的方法是将该类的实例注入第二个类,并且这需要您拥有一个非静态的类。 You still have some pain with respect to testing -- you might need to build in some code you can invoke with reflection to delete the singleton for test purposes. 关于测试,您仍然有些痛苦-您可能需要构建一些可以通过反射调用的代码,以便出于测试目的删除单例。 You can also use interfaces (though that would explicitly would allow the use of something other than the singleton by another developer) and simply provide the singleton as an instance of the interface to the class that uses it. 您还可以使用接口(尽管这将明确允许其他开发人员使用单例以外的其他东西),并简单地将单例提供为使用它的类的接口实例。

One consideration is that making a singleton an instance allows you to implement an interface. 一个考虑因素是,使一个实例成为实例可以使您实现接口。 Just because you want to control instantiation to it, does not mean you want every piece of code to know that it's a singleton. 仅仅因为您想控制它的实例化,并不意味着您希望每段代码都知道它是单例的。

For example, imagine you had a connection provider singleton that creates DB connections. 例如,假设您有一个连接提供程序单例创建数据库连接。

public class DBConnectionProvider implements ConnectionProvider {}

If it were a class with static methods, you couldn't inject the dependency, like this: 如果它是带有静态方法的类,则无法注入依赖项,如下所示:

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

It would have to be 一定是

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

Dependency injection is useful if you later want to unit test your method (you could pass in a mocked connection provider instead) among other things. 如果您以后要对方法进行单元测试(可以传递模拟连接提供程序),则依赖注入很有用。

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

When would you need to use one? 什么时候需要使用? There are many objects we only need one of: thread pools, caches, dialog boxes, objects that handle preferences and registry settings, objects used for logging, and objects that act as device drivers to devices like printers and graphic cards. 我们仅需要许多对象之一:线程池,缓存,对话框,处理首选项和注册表设置的对象,用于日志记录的对象以及充当打印机和图形卡等设备的设备驱动程序的对象。 For many of these types of object if we were to intantiate more than one we would run intol all sorts of problems like incorrect program behavior or overuse of resources 对于许多此类对象,如果我们要实例化多个对象,则会遇到各种问题,例如程序行为不正确或资源过度使用

Regarding the usage of synchronization it is definitly expensive and the only time syncrhonization is relevant is for the first time or the unique instatiation once instantiated we have no further need to synchronize again. 关于同步的使用,无疑是昂贵的,并且同步是唯一相关的时间,这是第一次,或者一旦实例化了唯一的实例,我们就不再需要再次同步。 After the first time through, syncrhonization is totally unneeded overhead :( 第一次使用之后,完全不需要同步:

Some people think that singletons are useless and dangerous. 有人认为单身人士毫无用处且危险。 I don't completely agree, but some points about it are valid, and singletons should be used carefully. 我并不完全同意,但是关于它的某些观点是正确的,应该谨慎使用单例。

Static classes used in place of singletons are even worse in my opinion. 在我看来,用于代替单例的静态类甚至更糟。 Static classes should be mainly used to group related functions which don't share a common resource. 静态类应主要用于对不共享公共资源的相关功能进行分组。 java.util.Collections is a good example. java.util.Collections是一个很好的例子。 It's just a bunch of functions that aren't tied to any object. 只是一堆不绑定任何对象的函数。

Singletons, on the other hand, are true objects. 另一方面,单例是真实的对象。 Ideally, singleton should be implemented without singleton pattern in mind. 理想情况下,应在实现单例模式时不考虑单例模式。 This will allow you to easily switch to using multiple instances if you suddenly need it. 如果您突然需要,这将使您可以轻松切换到使用多个实例。 For example, you may have only one database connection, but then you have to work with another, completely unrelated database at the same time. 例如,您可能只有一个数据库连接,但是随后您必须同时使用另一个完全不相关的数据库。 You just turn your singleton into a "doubleton" or "tripleton" or whatever. 您只需将单身人士变成“双身人士”或“ tripleton”之类的人。 If you need, you can also make its constructor public which will allow creation of as many instances as you want. 如果需要,还可以将其构造函数公开,这将允许创建所需数量的实例。 All this stuff isn't so easy to implement with static classes. 使用静态类很难实现所有这些东西。

Simply put, a singleton is a regular class that has one instance globally available, to save you from all the trouble of passing it everywhere as a parameter. 简而言之,单例是一个具有全局可用实例的常规类,从而避免了将其作为参数传递到任何地方的麻烦。

And there is not much trouble in creating a singleton. 创建一个单例并没有太大的麻烦。 You just create a regular class with a private constructor, then implement one factory method, and you're done. 您只需使用私有构造函数创建一个常规类,然后实现一个工厂方法即可。

Others have provided better answers while I was replying, but I'll leave my answer for posterity. 在我回复时,其他人提供了更好的答案,但是我将其答案留给后代使用。 It would appear unit testing is the big motivator here. 看起来单元测试是这里的主要动机。

For all intents and purposes, there is no reason to prefer a singleton to the approach you described. 出于所有意图和目的,没有理由比您描述的方法更喜欢单例。 Someone decided that static variables are capital-b Bad (like other sometimes useful features like gotos) because they're not far off from global data and in response we have the workaround of singletons. 有人认为静态变量是大写的-B Bad(就像其他有时有用的功能,如gotos)一样,因为它们与全局数据相距不远,因此,我们有单例的解决方法。

They are also used with other patterns. 它们也与其他模式一起使用。 See Can an observable class be constructed as a singleton? 请参阅可观察类是否可以构造为单例?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么需要使用多个构造函数? - Why would you need to use more than one constructor? 为什么在 Java 中需要无符号类型? - Why would you need unsigned types in Java? 您何时以及为什么会使用 Java 的供应商和消费者接口? - When and why would you use Java's Supplier and Consumer interfaces? 为什么在Java或Android中,单例类需要依赖注入? - Why do we need Dependency Injection for singleton classes in java or Android? 为什么你想要一个没有声明公共类的Java文件? - Why would you ever want a Java file with no public classes declared in it? 什么是动态代理类,为什么我会使用它? - What are Dynamic Proxy classes and why would I use one? 为什么你会在java中使用BitSet而不是布尔数组(在Java中)? - Why would you use a BitSet in java as opposed to an array of booleans (in Java)? 为什么在扩展 Java class 时要使用泛型类型说明符? - Why would you use a generic type specifier when extending a Java class? 为什么要使用`java.util.function.supplier`,只要调用一个方法呢? - Why would one use a `java.util.function.supplier`, when one can just call a method? 当我已经导入了它们所在的 package 时,为什么还需要导入枚举类才能使用它们? [爪哇] - Why do I need to import enum classes to use them when I already imported the package they're in? [Java]
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM