题 静态类和单例模式之间的区别?


静态类和单例模式之间存在什么真正的(即实际的)差异?

两者都可以在没有实例化的情况下调用,两者都只提供一个“实例”,它们都不是线程安全的。还有其他区别吗?


1493
2018-02-06 08:13


起源


根据语言实现和您的使用模式,单身人士 可能 由于调用的开销而效率较低 getInstance() 每次你想要使用它的方法(虽然可能在大多数情况下 没关系)。 - too much php
已经有很多答案了。它实际上是一个 singleton 对象在哪里 static 方法只是函数,非OO实体。 - fastcodejava
取决于实施.. csharpindepth.com/Articles/General/Singleton.aspx - VJAI
如果要允许第三方提供类的实现,则会有所不同。在这种情况下,您通常也需要工厂模式。看到 agiletribe.wordpress.com/2013/10/08/... - AgilePro
IMO这个答案总结得很好 stackoverflow.com/questions/14097656/... - Dave


答案:


是什么让你说单例或静态方法不是线程安全的?通常都是 应该 实现是线程安全的。

单例和一堆静态方法之间的最大区别在于单例可以实现接口(或者从有用的基类派生,尽管根据我的经验这不太常见),所以你可以传递单例,好像它只是“另一个” “实施。


1044
2018-02-06 08:17



好吧,如果你喜欢它,它们本身都不是线程安全的,你必须使它们都是线程安全的,所以两者都没有区别。 - Jorge Córdoba
你能给出一个例子吗? 是 固有线程安全,除了不可变类型? - Jon Skeet
对Skeet:人们说单例不是线程安全意味着线程之间共享一个单例 不必要的 所有的时间,当你需要堆栈对象时,它们会被共享,这意味着你不必进行不必要的同步。
@Geek:想象一下,单身人士实现了一个界面 Foo,你有一个方法采取 Foo 作为参数。通过该设置,调用者可以选择使用单例作为实现 - 或者他们可以使用不同的实现。该方法与单例解耦。将其与类只有静态方法的情况相比较 - 每个想要调用这些方法的代码都与类紧密耦合,因为它需要指定哪个类包含静态方法。 - Jon Skeet
@AmirBareket:根据单身设计模式,它不是单身人士 - 如果类本身允许创建多个实例,那么它不是单身IMO,无论工厂做什么。 - Jon Skeet


真正的答案是Jon Skeet, 在另一个论坛上

单身人士允许访问单身   创建实例 - 该实例(或   相反,对该实例的引用)   可以作为参数传递给其他人   方法,并视为正常   目的。

静态类只允许静态   方法。


404
2018-02-06 08:21



但是,如果可以通过调用静态getInstance()方法从几乎任何地方访问同一个实例,为什么要将Singleton作为参数传递? - Henrique Ordine
@HenriqueOrdine所以它可以适应现有的代码并提供一个接口?
@HenriqueOrdine他们讲的是静态类,而不是静态方法的类。静态类无法实例化。但是,如果传递包含静态方法的(非静态)类的实例,则无法在实例上调用静态方法。 - Goran
什么是静态类?至少在Java中,没有这样的东西。 - Henrique Ordine
@Goran我最初对你的措辞很困惑。你说“你不能在一个实例上调用静态方法”。我把它读作“如果你有一个实例化对象的引用,你就不能调用它可能有的任何静态方法。”那当然是不正确的。再次阅读它几次之后我认为你的意思是“从静态方法内部你无法访问类中的非静态对象”这是正确的。想要澄清那些对这些概念不熟悉的人谁会遇到这个答案并阅读你的评论。 - Andrew Steitz


  1. 单例对象存储在 ,但静态对象存储在
  2. 我们可以 克隆 (如果设计者没有禁止它)单例对象,但我们无法克隆静态类对象 。
  3. 单身人士课程遵循 OOP (面向对象的原则),静态类没有。
  4. 我们可以实施一个 interface 使用Singleton类,但是类的静态方法(或者例如C# static class)不能。

325
2017-09-11 10:22



第二个说法是错误的。我们无法克隆Singleton对象。 Singleton实现必须拒绝这一点。如果你真的可以克隆Singleton,那就不是Singleton。 - Alexander Yancharuk
这对Java来说是不正确的:单例和静态都不使用堆栈。 - AgilePro
#1并不重要。 #2描述了有缺陷的实现。 #3完全没有道理。 - Casey
静态对象如何存储在堆栈中?调用方法时会创建新的堆栈帧,它会存储方法的局部变量,当方法返回时会删除此堆栈帧,并且这些局部变量会丢失。确保堆栈速度快,但不适合存储静态对象。 - mike_m
我无法理解这一点的赞成数量。 1)为什么Singleton必须存储在堆栈中?在C#等托管语言中,Java数据存储在托管堆中,本地方法变量/参数除外。 2)如果你可以克隆它,那么它不是一个正确实现的单例。 3)Singleton被称为OOP反模式;即,如果可能,你应该避免的事情。 4)这是唯一正确的事情。 - Groo


Singleton模式比静态类有几个优点。首先,单例可以扩展类和实现接口,而静态类不能(它可以扩展类,但不会继承它们的实例成员)。单例可以懒惰或异步初始化,而静态类通常在首次加载时初始化,从而导致潜在的类加载器问题。然而,最重要的优点是单例可以多态处理,而不会强迫用户假设只有一个实例。


122
2018-02-06 08:30



+1为好,务实的要点。单身模式通常被过度使用,但有一些情况是适合的。也可以看看: agiletribe.wordpress.com/2013/10/08/... - AgilePro
+1表示急切初始化静态类。 - Arpit Khandelwal
你是多态的优势是正确的。这是最重要的一点 - Ahmad
嵌套静态类可以实现接口。尝试编码,会起作用。我可以编译代码没有任何错误。 - nanosoft


static 类不应该做任何需要状态的东西,它有助于将一堆函数放在一起,即 Math (要么 Utils 在项目中)。因此,班级名称只是给我们一个线索,我们可以在哪里找到这些功能,而且仅此而已。

Singleton 是我最喜欢的模式,并使用它来管理单点的东西。它比以前更灵活 static 类和可以维持状态。它可以实现接口,从其他类继承并允许继承。

我的选择规则 static 和 singleton

如果有一堆功能应该保持在一起,那么 static 是选择。 可以实现任何其他需要单独访问某些资源的东西 singleton


56
2017-12-30 20:55



为什么静态类不能做任何需要保存状态的东西? - Trisped
@Trisped:您既没有精确控制初始化也没有最终确定。 - Xaqron
Jon双向飞碟回答得很好。它的差异点不大。 - Gul Md Ershad
你失去了我“单身人士是我最喜欢的模式”。单身人士是一个如此尖锐的角落,它应该被视为反模式和模式。类可以具有静态状态,这也是单一访问,如果任何静态状态比单例更“单一访问”,因为大多数单例实现都被破坏,即。你可以克隆单例,而静态被定义祝福是唯一的。 - PoweredByRice
@KyleDelaney:简单 State 是对象的不同属性的组合,通常随时间变化。你可以谷歌正式定义。 - Xaqron


静态类: -

  1. 您无法创建静态类的实例。

  2. 加载包含类的程序或命名空间时,由.NET Framework公共语言运行库(CLR)自动加载。

  3. 静态类不能有构造函数。

  4. 我们不能将静态类传递给方法。

  5. 我们不能将Static类继承到C#中的另一个Static类。

  6. 具有所有静态方法的类。

  7. 更好的性能(静态方法在编译时绑定)

辛格尔顿: -

  1. 您可以创建该对象的一个​​实例并重用它。

  2. Singleton实例是在用户请求时首次创建的。

  3. Singleton类可以有构造函数。

  4. 您可以创建singleton类的对象并将其传递给method。

  5. Singleton类没有说继承的任何限制。

  6. 我们可以处理单例类的对象而不是静态类的对象。

  7. 方法可以被覆盖。

  8. 可以在需要时延迟加载(始终加载静态类)。

  9. 我们可以实现接口(静态类不能实现接口)。


49
2018-06-01 11:42



静态类有构造函数: msdn.microsoft.com/en-us/library/k9x6w0hc.aspx - Tomer Arazy
是的,static可以具有该类内部的构造函数。当调用类中的任何静态方法时,将调用此方法。 - rahulmr
对于编译时的单例,它存储在HEAP内存中,但如果它被实例化,它会被存储在STACK中吗? - Luminous_Dev
@Luminous_Dev否。任何单例实例都是一天结束时的对象实例。毫无疑问,它将存储在堆上。 - RBT


静态类是仅具有静态方法的类,对于该类,更好的单词将是“函数”。静态类中体现的设计风格纯粹是程序性的。

另一方面,Singleton是OO设计特有的模式。它是一个对象的实例(具有其中固有的所有可能性,例如多态性),具有创建过程,该过程确保在其整个生命周期中只有该特定角色的一个实例。


48
2018-02-06 08:35



多态性根本不会与单身人士发挥作用
所以你认为。我的想法不同。 ;)例如,想象一个返回接口的单件工厂。你知道你正在获得一个ISingleton(它永远是同一个),但不一定是哪个实现。 - Morendil
嵌套的静态类也可以有实例方法,它不限于只有静态方法。编码它,你可以看到。 - nanosoft
在具有更好的对象模型的语言(例如Ruby)中,类也是对象。静态类的“纯粹程序”方面是语言强加的任意限制。 - Max


在单例模式中,您可以将单例创建为派生类型的实例,但不能使用静态类。

快速示例:

if( useD3D )
    IRenderer::instance = new D3DRenderer
else
    IRenderer::instance = new OpenGLRenderer

32
2018-02-06 08:16



它不是真正的单身模式,看起来更像工厂给我。 - vava
不是真的,两者之间的根本区别在于,Singleton将“缓存”其单个对象并继续返回(引用)同一个对象。 Factory模式将创建新实例。 - Mystic
然后是代理单身:) - vava
嗯,我知道Singleton作为MonoState的各种各样。 - Huppie
例子是工厂模式 - Rajavel D


要扩展 Jon Skeet的回答

单例和一组静态方法之间的最大区别在于单例可以实现接口(或者从有用的基类派生,虽然这不太常见的IME),因此您可以传递单例,就像它是“只是另一个”实现一样。

单元测试课程时,单身人士更容易使用。无论您将单例作为参数(构造函数,设置器或方法)传递,您都可以替换单例的模拟或存根版本。


22
2017-12-20 21:53



我不认为你可以直接嘲笑一个单身人士。难道你不必声明singleton和mock类都实现的接口吗? - Ellen Spertus
@espertus为什么你不能嘲笑你的单身人士?使用mockito的示例 MySingleton mockOfMySingleton = mock(MySingleton.class)。 - Mike Rylander
你是对的,你可以使用像mockito这样使用反射的工具来嘲笑它。我的意思是你不能通过继承它并重写它的方法来直接模拟它。 - Ellen Spertus
@espertus为什么不呢?当您实例化您正在测试的对象时,无论您使用原始对象,都可以替换单例的子类实现。例如: new ClazzToTest(mockSingleton); - Mike Rylander
我没有使用过Mockito,但你如何将具有私有构造函数的类子类化,这是单例的情况,除了使用反射?相关讨论: stackoverflow.com/questions/2302179/mocking-a-singleton-class  stackoverflow.com/questions/15939023/... - Ellen Spertus