Java中的单例模式和静态类有啥区别? [复制]

Posted

技术标签:

【中文标题】Java中的单例模式和静态类有啥区别? [复制]【英文标题】:What is the difference between a Singleton pattern and a static class in Java? [duplicate]Java中的单例模式和静态类有什么区别? [复制] 【发布时间】:2011-04-01 17:20:29 【问题描述】:

单例与仅填充静态字段的类有何不同?

【问题讨论】:

您是在问“实际的区别是什么?” 我只是对两者的一般差异感到好奇,即 Singleton 是否有一些独特之处,使其不同于将所有方法和属性设置为静态的类? DCL 实现单例的方式只有静态字段实例持有对单例对象的静态引用,然后在静态方法中延迟实例化。在这种情况下,这个 Singleton 实现只有静态字段和方法,它们是相同的(正如问题所问的不同)。这是一种实现。其他的可以通过Enum。 【参考方案1】:

几乎每次我编写一个静态类时,我最终都希望我将它实现为一个非静态类。考虑:

可以扩展非静态类。多态性可以节省大量重复。 非静态类可以实现接口,当您想将实现与 API 分开时,它可以派上用场。

由于这两点,非静态类使得为依赖它们的项目编写更可靠的单元测试成为可能。

然而,单例模式离静态类只有半步之遥。您有点获得了这些好处,但是如果您通过“ClassName.Instance”直接在其他类中访问它们,那么您正在为获得这些好处制造障碍。就像 ph0enix 指出的那样,使用依赖注入模式会更好。这样,一个 DI 框架可以被告知一个特定的类是(或不是)一个单例。您可以获得模拟、单元测试、多态性和更多灵活性的所有好处。

【讨论】:

在java中,嵌套的静态类可以扩展其他类(也可以被其他类扩展)并实现接口。检查***.com/a/37114702/1406510 @nanosoft:当我说“静态类”时,我指的不是 Java 的 "static nested classes",而是 OP 所询问的“仅填充静态字段的类”。如果您考虑使用静态方法,无论它们所在类的性质如何,我在这里所做的所有陈述都适用:它们不能实现接口或扩展。很抱歉我说“静态类”可能引起任何混淆——这是一个在其他语言中更常见的术语,但不适用于 Java。 如果我没记错的话,Junit 似乎非常严重地忽略了静态初始化。【参考方案2】:

让我总结一下:)

本质区别是:单例的存在形式是对象,静态不是。这导致了以下事情:

可以扩展单例。不是静态的。 如果没有正确实现,单例创建可能不是线程安全的。不是静态的。 单例可以作为对象传递。不是静态的。 可以对单例进行垃圾收集。不是静态的。 单例优于静态类! 这里有更多,但我还没有意识到:)

最后但同样重要的是,无论何时你要实现一个单例,请考虑重新设计你的想法,不要使用这个上帝对象(相信我,你会倾向于把所有“有趣”的东西放到这个类中)并使用一个名为“Context”或类似名称的普通类。

【讨论】:

+1 表示“上帝对象”原则。喜欢它。 在java中,嵌套的静态类可以扩展其他类(也可以被其他类扩展)。静态嵌套类可以作为对象传递。它也可以被垃圾收集。检查***.com/a/37114702/1406510 静态类可以是线程安全的,也可以不是线程安全的.. 我对第 2 点的 not in 很感兴趣,您是在分配方式使用它,还是针对谓词或从属子句?你是说“如果没有正确实现,静态可能是线程安全的”?或者你是说“如果正确实现静态可能是线程安全的?”或者您是说“如果正确实施,静态可能不是线程安全的”?你能澄清一下你所说的关于静态的不同之处吗,也许是一个代码示例? 这是最简洁的答案 【参考方案3】:

单例可以懒惰地初始化。

【讨论】:

@Tom Hawtin - tackline:你这是什么意思? 静态字段并不是真正的延迟加载。类加载器加载类时会加载静态字段。可以编写单例,这样单例实例不会在类加载时初始化,而是在对单例实例的第一次请求时初始化。当然,单例最常用的方法并不是懒惰。【参考方案4】:

我认为,重要的是面向对象编程中的“对象”。除了少数情况,我们应该限制使用静态类。这些情况是:

    当创建一个对象是没有意义的。类似于 java.lang.Math 的方法。我们可以像使用对象一样使用类。因为 Math 类方法的行为不依赖于要在该类中创建的对象的状态。 由多个对象方法联合使用的代码,未到达对象变量且可能被关闭的代码可以是静态方法

另一个重要的事情是单例是可扩展的。单例可以扩展。在 Math 类中,使用 final 方法,避免了该类对象的创建和扩展。 java.lang.System 类也是如此。但是,Runtime 类是单个对象,而不是静态方法。在这种情况下,您可以为不同的目的覆盖 Runtime 类的继承方法。

您可以延迟创建单例对象,直到需要它(延迟加载)。但是,对于静态方法类,没有条件之类的东西。如果您到达该类的任何静态成员,该类将被加载到内存中。

因此,静态方法类最基本的好处是您不必创建对象,但是如果使用不当,它将使您的代码不再面向对象。

【讨论】:

#1 应该在上下文中使用。拨打“毫无意义”的Date.Now 电话。然而,在 99.99% 的情况下,使用静态 Date.Now 方法调用几乎不可能进行模拟测试。或者更确切地说,静态方法不允许通常允许注入和模拟。【参考方案5】:

区别与语言无关。根据定义,单例是:“确保一个类只有一个实例并提供对其的全局访问点。”仅填充静态字段的类与单例不同,但在您的使用场景中,它们可能提供相同的功能。但正如 JRL 所说,懒惰的启动是其中一个区别。

【讨论】:

【参考方案6】:

至少您可以更轻松地用模拟或存根替换它以进行单元测试。但我不是单身人士的忠实粉丝,正如你所描述的原因:它是伪装的全局变量。

【讨论】:

其实我觉得单例可以帮助你减少全局变量的数量。你有一个对象的引用。它可以将所有静态字段包装到其中。 @BenoitParis - 它减少了全局变量的绝对数量,但没有解决与它们相关的任何弊端。 一开始就不应该有全局变量。即使拥有静态方法也不是真正的 OOP。 @D.Shawley 相当。单例只是增加了更多的复杂性,同时没有解决任何引起的问题。 使某物成为单例(假设服务器端编程)的最简洁方法是通过 Spring 应用程序上下文声明一个命名 bean。如果您设置 scope="singleton" 则重复调用 appContext.getBean() 会返回相同的实例。如果你设置 scope="prototype" 那么每次调用都会返回一个新实例。意味着代码不必实现或担心 getInstance(),您只需获取 bean。单例是默认的,因为无论如何大多数 bean 都是单例。 【参考方案7】:

一个单例类将有一个实例,通常每个类加载器只有一个。所以它可以有常规方法(非静态),并且可以在该特定实例上调用它们。

虽然类只有静态方法,但实际上没有必要创建实例(因此大多数人/框架都将这类 Util 类抽象化)。您将直接调用类上的方法。

【讨论】:

【参考方案8】:

首先想到的是,如果您想使用仅具有静态方法和属性的类而不是单例,则必须使用静态初始化程序来正确初始化某些属性。示例:

class NoSingleton 
  static 
    //initialize foo with something complex that can't be done otherwise
  
  static private foo;

这将在类加载时执行,这可能不是您想要的。如果您将其实现为单例,则您可以更好地控制整个 shebang。但是我认为在任何情况下使用单例都不是一个好主意。

【讨论】:

当我们对他可能使用它们的上下文一无所知时,我认为你说单例“不是一个好主意”,有点轻率。编程没有绝对性,几乎总是有一个用例使一个或另一个成为最好的。这就像告诉某人“鸭子”这个词总是指一种动物。但是上下文很重要,因为它可能意味着完全不同的东西,比如“当有人向你表演时鸭子”。附言但我没有对你投反对票。【参考方案9】:

单例是一个只有一个实例的类,强制执行。该类可能具有状态(是的,我知道静态变量保持状态),并非所有成员变量或方法都需要是静态的。

变体是这些对象的一个​​小池,如果所有方法都是静态的,这是不可能的。

【讨论】:

问题询问的是字段,而不是方法。 一小群单例对象?我想这是有道理的,对于一些“小”的价值。【参考方案10】:

注意:这些示例是用 C# 编写的,因为这是我更熟悉的,但这个概念应该同样适用于 Java。

忽略关于何时适合使用 Singleton 对象的争论,我知道的一个主要区别是 Singleton 对象有一个可以传递的实例。

如果您使用静态类,您将自己硬连接到特定实现,并且无法在运行时更改其行为。

使用静态类的糟糕设计:

public class MyClass

   public void SomeMethod(string filename)
   
      if (File.Exists(filename))
        // do something
   

或者,您可以让构造函数接受特定接口的实例。在生产中,您可以使用该接口的 Singleton 实现,但在单元测试中,您可以简单地模拟接口并更改其行为以满足您的需求(例如,使其抛出一些模糊的异常)。

public class MyClass

   private IFileSystem m_fileSystem;

   public MyClass(IFileSystem fileSystem)
   
      m_fileSystem = fileSystem;
   

   public void SomeMethod(string filename)
   
      if (m_fileSystem.FileExists(filename))
         // do something
   

这并不是说静态类总是不好的,只是不适合文件系统、数据库连接和其他低层依赖项。

【讨论】:

【参考方案11】:

单例的主要优点之一是您可以实现接口并从其他类继承。有时你有一组单例,它们都提供类似的功能,你想实现一个公共接口,但负责不同的资源。

【讨论】:

嵌套静态类也可以扩展另一个类并实现接口。 ***.com/a/37114702/1406510【参考方案12】:

单例类: 单例类是每个类加载器只能存在单个实例的类。

助手类(只有静态字段/方法的类): 不存在此类的实例。只有字段和方法可以作为常量或辅助方法直接访问。

blog 中的这几行很好地描述了它:

首先,单例模式非常 如果你想创建一个很有用 类的实例。为了我的帮手 我们真的不想上课 实例化类的任何副本。 你不应该使用的原因 单例类是因为这个 我们不使用任何辅助类 变量。单例类将 如果它包含一组 我们只想要一组的变量 和使用的方法 变量,但在我们的帮助类中,我们 除了 传入的(我们将其设为最终版本)。 因此,我不相信我们 想要一个单例实例,因为我们 不想要任何变量,我们不想要 希望任何人实例化这个类。 所以如果你不想要任何人 实例化类,即 通常如果你有某种 helper/utils 类然后我使用什么 我称静态类,一个类 一个私有构造函数,并且只有 由无任何静态方法组成 任何变量。

【讨论】:

以上是关于Java中的单例模式和静态类有啥区别? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Java中单例模式和静态类的区别

Android中的单例模式

请问java 单例类 与 静态类 有何不同?

kotlin用object实现单例模式,companion object与java静态

kotlin用object实现单例模式,companion object与java静态

Java中的单例模式