Java中的单模式Singletons和静态类的区别是什么?

What is the difference between a Singleton pattern and a static class in Java?

本问题已经有最佳答案,请猛点这里访问。

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


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

  • 可以扩展非静态类。多态性可以节省很多重复。
  • 非静态类可以实现一个接口,当您想将实现与API分离时,这个接口非常有用。

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

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


让我总结一下:)

本质的区别是:单子的存在形式是一个对象,而静态的则不是。这导致了以下情况:

  • 单件可以扩展。静态不是。
  • 如果没有正确实现,则单例创建可能不是线程安全的。静态不是。
  • singleton可以作为对象传递。静态不是。
  • 单件可以被垃圾收集。静态不是。
  • 单件比静态类好!
  • 更多,但我还没意识到:)

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


一个单例可以被懒惰地初始化。


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

  • 当创建对象没有意义时。类似java.lang.math的方法。我们可以像对象一样使用类。因为数学类方法的行为不依赖于要在此类中创建的对象的状态。
  • 由多个对象方法共同使用的代码,不到达对象变量并且可能被关闭的代码可以是静态方法。
  • 另一件重要的事情是singleton是可扩展的。单件可以扩展。在数学类中,使用final方法可以避免创建和扩展该类的对象。java.lang.system类也是如此。但是,运行时类是单个对象,而不是静态方法。在这种情况下,可以为不同的目的重写运行时类的继承方法。

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

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


    区别在于语言独立。singleton的定义是:"确保一个类只有一个实例,并提供对它的全局访问点。"只填充静态字段的类与singleton不同,但在您的使用场景中,它们可能提供相同的功能。但正如JRL所说,懒惰的开始是一个区别。


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

    虽然类只包含静态方法,但实际上不需要创建实例(因为这个原因,大多数人/框架都将这些类型的util类抽象化)。您将直接调用类上的方法。


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

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


    至少您可以更容易地用单元测试的模拟或存根替换它。但我不太喜欢单子,这正是你描述的原因:它是伪装的全局变量。


    首先要想到的是,如果要使用只包含静态方法和属性的类而不是单例类,则必须使用静态初始值设定项来正确初始化某些属性。例子:

    1
    2
    3
    4
    5
    6
    class NoSingleton {
      static {
        //initialize foo with something complex that can't be done otherwise
      }
      static private foo;
    }

    然后在类加载时执行,这可能不是您想要的。如果您将整个shebang作为一个单例实现,那么您可以更好地控制它。不过,我认为在任何情况下使用单件并不是一个好主意。


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

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

    这篇博文中的几行很好地描述了它:

    Firstly the Singleton pattern is very
    useful if you want to create one
    instance of a class. For my helper
    class we don't really want to
    instantiate any copy's of the class.
    The reason why you shouldn't use a
    Singleton class is because for this
    helper class we don't use any
    variables. The singleton class would
    be useful if it contained a set of
    variables that we wanted only one set
    of and the methods used those
    variables but in our helper class we
    don't use any variables apart from the
    ones passed in (which we make final).
    For this reason I don't believe we
    want a singleton Instance because we
    do not want any variables and we don't
    want anyone instantianting this class.
    So if you don't want anyone
    instantiating the class, which is
    normally if you have some kind of
    helper/utils class then I use the what
    I call the static class, a class with
    a private constructor and only
    consists of Static methods without any
    any variables.


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


    注意:例子是C语言,这是我比较熟悉的,但是这个概念应该适用于Java。

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

    如果您使用一个静态类,您将自己硬连接到一个特定的实现,并且没有办法在运行时改变它的行为。

    使用静态类的不良设计:

    1
    2
    3
    4
    5
    6
    7
    8
    public class MyClass
    {
       public void SomeMethod(string filename)
       {
          if (File.Exists(filename))
            // do something
       }
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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
       }
    }

    这并不是说静态类总是不好的,只是对于诸如文件系统、数据库连接和其他较低层依赖性之类的东西来说,它不是一个很好的候选者。