Java 为什么使用单例而不是静态方法?

Java 为什么使用单例而不是静态方法?,java,design-patterns,singleton,Java,Design Patterns,Singleton,对于这些关于助手/实用程序类的简单问题,我从来没有找到好的答案: 为什么我要创建一个单例(无状态)而不是使用静态方法 如果对象没有状态,为什么需要对象实例 通常,单例用于向应用程序引入某种全局状态。(老实说,这不是真正必要的,但这是另一个时代的话题。) 但是,在一些极端情况下,即使是无状态的单例也可能有用: 您希望在可预见的未来通过state进行扩展 由于某些特定的技术原因,您需要一个对象实例。示例:用于C#lock或Javasynchronized语句的同步对象 您需要继承,也就是说,您希

对于这些关于助手/实用程序类的简单问题,我从来没有找到好的答案:

  • 为什么我要创建一个单例(无状态)而不是使用静态方法
  • 如果对象没有状态,为什么需要对象实例

通常,单例用于向应用程序引入某种全局状态。(老实说,这不是真正必要的,但这是另一个时代的话题。)

但是,在一些极端情况下,即使是无状态的单例也可能有用:

  • 您希望在可预见的未来通过state进行扩展
  • 由于某些特定的技术原因,您需要一个对象实例。
    示例:用于C#
    lock
    或Java
    synchronized
    语句的同步对象

  • 您需要继承,也就是说,您希望能够轻松地用另一个使用相同接口但不同实现的单例替换您的单例。
    示例:Java中的
    Toolkit.getDefaultToolkit()
    方法将返回一个单例,其确切类型取决于系统
  • 您希望.br>示例:
    DBNull.Value
    在C#中的引用相等

我可以看到使用无状态单例而不是静态方法类的情况,即for

如果您有一个直接使用的实用程序函数的助手类,它会创建一个隐藏的依赖项;你无法控制谁可以使用它,也无法控制在哪里使用它。通过无状态单例实例注入相同的helper类,可以控制它的使用位置和方式,并在需要时替换它/模拟它/等等


将其设置为单例实例可以简单地确保您不会分配任何超出需要的类型的对象(因为您只需要一个)

在大多数编程语言中,类避开了许多类型系统。虽然一个类及其静态方法和变量是一个对象,但它通常无法实现接口或扩展其他类。因此,它不能以多态方式使用,因为它不能是其他类型的子类型。例如,如果您有一个接口
IFooable
,该接口是其他类的多个方法签名所必需的,那么类对象
StaticFoo
不能代替
IFooable
,而
FooSingleton.getInstance()
可以(假设,
FooSingleton
实现
IFooable

请注意,正如我对Heinzi的回答所评论的,singleton是一种控制实例化的模式。它将
newclass()
替换为
Class.getInstance()
,这使
Class
的作者对实例有了更多的控制,他可以使用这些控制来防止创建不必要的实例。单例只是工厂模式的一个非常特殊的情况,应该这样对待。通用性使其成为全球注册中心的特例,而这种情况往往以糟糕的结果告终,因为全球注册中心不应该被随意使用

如果您计划提供全局助手函数,那么静态方法就可以正常工作。该类将不作为类,而只是作为名称空间。我建议您保持高内聚性,否则可能会出现最奇怪的耦合问题

格里茨

back2dos

单例不是无状态的,它持有全局状态

我认为使用Singleton的一些原因是:

  • 避免内存泄漏
  • 为应用程序中的所有模块(如数据库连接)提供相同的状态

事实上,我发现了另一个这里没有提到的答案:静态方法更难测试


似乎大多数测试框架在模拟实例方法方面都非常有效,但其中许多框架并没有以适当的方式处理静态方法的模拟

在使用哪一种方法之间需要权衡。单例可以有状态,也可以没有状态,它们引用的是对象。如果它们不保持状态并且只用于全局访问,那么静态方法更好,因为这些方法会更快。但是如果您想利用对象和OOP概念(继承多态性),那么singleton更好

考虑一个例子:java.lang.Runtime是java中的一个单例类。这个类允许每个JVM有不同的实现。每个JVM的实现是单一的。如果这个类是静态的,我们就不能传递基于JVM的不同实现

我发现这个链接非常有用:

希望能有帮助

对于我来说“想要对象状态使用单例,想要函数使用静态方法”

这取决于你想要什么。无论何时需要对象状态(例如,多态性,如Null状态,而不是
Null
,或默认状态),singleton都是合适的选择,而静态方法在需要函数时使用(接收输入,然后返回输出)

我建议对于singleton的情况,它在实例化之后应该始终保持相同的状态。它既不应该是可关闭的,也不应该接收任何要设置的值(来自文件的静态配置除外,例如java中的属性文件)

另外,这两种模式的性能以毫秒为单位是不同的,因此首先关注架构。

摘自GoF的《设计模式》一书中的“单件”一章:

  • 比类操作更灵活。另一种封装单点功能的方法是使用类操作(即C++中的静态成员函数或SimultSalk中的类方法)。但是,这两种语言技术都使得很难改变设计以允许一个类的多个实例。此外,C++中的静态成员函数不是虚拟的,所以子类不能以多态方式重写它们。
    我将使用+1,尽管IMHO单态被误用来引入全球状态。单例的目的不是使对象全局可用,而是强制对象处于可用状态