静态类和单例模式之间存在什么真正(即实用)的区别?

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


#1楼

  1. 延迟加载
  2. 支持接口,因此可以提供单独的实现
  3. 能够返回派生类型(作为延迟加载和接口实现的组合)

#2楼

当我想要具有全部功能的类时,例如,有许多方法和变量,我使用单例;

如果我想要只包含一个或两个方法的类,例如MailService类,它只有1个方法SendMail(),则我使用静态类和方法。


#3楼

在很多情况下,这两者没有实际区别,尤其是在单例实例从不改变或非常缓慢地改变(例如保持配置)的情况下。

我想说的最大区别是,单例仍然是普通的Java Bean,与专门针对静态的Java类相反。 因此,在更多情况下可以接受单例。 实际上,它是默认的Spring Framework的实例化策略。 消费者可能知道也可能不知道这是一个单例,只是像对待普通Java bean一样对待它。 正如我们在Spring中经常看到的那样,如果需求发生变化,而需要将一个单一实例变成原型,则可以完全无缝地完成它,而无需对使用者进行任何代码更改。

之前有人提到静态类应该纯粹是过程性的,例如java.lang.Math。 在我看来,此类绝不应该被传递,并且除了静态final以外,它们不应该包含任何其他属性。 对于其他所有内容,请使用单例,因为它更加灵活且易于维护。


#4楼

我们拥有连接后端的数据库框架。为避免多个用户之间的脏读,我们使用单例模式来确保我们在任何时间都可以使用单个实例。

在c#中,静态类无法实现接口。 当单个实例类需要实现用于业务合同或IoC目的的接口时,这是我在不使用静态类的情况下使用Singleton模式的地方

Singleton提供了一种在无状态方案中维护状态的方法

希望对您有帮助。


#5楼

一个显着的区别是Singletons附带的实例化不同。

对于静态类,它是由CLR创建的,我们无法对其进行控制。 如果是单例,则在尝试访问该对象的第一个实例上实例化该对象。


#6楼

  1. 辛格尔顿对象存储在 ,但静态对象存储在堆栈中
  2. 我们可以克隆 (如果设计者不允许)单例对象,但是我们不能克隆静态类对象。
  3. 单例类遵循OOP (面向对象原则),而静态类则不遵循。
  4. 我们可以使用Singleton类实现interface ,但是不能使用类的静态方法(例如C# static )。

#7楼

我不是一个很棒的面向对象理论家,但是据我所知,我认为静态类与Singletons相比唯一缺乏的OO特性是多态性。 但是,如果您不需要它,则使用静态类当然可以具有继承(不确定接口实现)以及数据和函数封装。

Morendil的评论“静态类中体现的设计风格纯粹是程序性的”,我可能是错的,但我不同意。 在静态方法中,您可以访问静态成员,这与访问其单个实例成员的单例方法完全相同。

编辑:
我现在实际上在想的另一个区别是,静态类在程序启动时实例化 ,并且在程序的整个生命周期中都存在,而单例则在某个时刻显式实例化,并且也可以销毁。

*或者我认为它可能会在首次使用时实例化,具体取决于语言。


#8楼

扩展Jon Skeet的答案

单例和一堆静态方法之间的最大区别在于,单例可以实现接口(或从有用的基类派生,尽管这是不太常见的IME),因此您可以将单例作为“只是另一个”实现来传递。

在单元测试课程时,单例更容易使用。 无论您在何处传递单例作为参数(构造函数,setter或方法),都可以替代模拟的或存根的单例版本。


#9楼

为了说明乔恩的观点,如果Logger是静态类,那么下面显示的内容将无法完成。该类SomeClass希望将ILogger实现的实例传递到其构造函数中。

单例类对于进行依赖注入很重要。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {

            var someClass = new SomeClass(Logger.GetLogger());
        }


    }

    public class SomeClass 
    {
        public SomeClass(ILogger MyLogger)
        {

        }
    }

    public class Logger : ILogger
    {
        private static Logger _logger;
        private Logger() { }

        public static Logger GetLogger()
        {
            if (_logger==null)
            {
                _logger = new Logger();
            }

            return _logger;
        }

        public void Log()
        {

        }

    }


    public interface ILogger
    {
         void Log();
    }
}

#10楼

在我写的一篇文章中,我描述了关于单例为什么比静态类好得多的观点:

  1. 静态类实际上不是规范类,它是具有函数和变量的名称空间
  2. 使用静态类不是一个好习惯,因为它违反了面向对象的编程原则
  3. 静态类不能作为其他类的参数传递
  4. 静态类不适用于“延迟”初始化
  5. 始终很难跟踪静态类的初始化和使用
  6. 实施线程管理很困难

#11楼

一种。 序列化-静态成员属于该类,因此无法序列化。

b。 尽管我们已经将构造函数设为私有,但是静态成员变量仍将被携带到子类中。

C。 我们不能进行延迟初始化,因为所有内容只会在类加载时加载。


#12楼

单个静态类实例(即,一个类的实例,恰好是一个静态或全局变量)与指向堆上该类实例的单个静态指针之间存在巨大差异:

当您的应用程序退出时,将调用静态类实例的析构函数。 这意味着,如果您将该静态实例用作单例,则单例将停止正常工作。 如果仍在运行使用该单例的代码(例如在其他线程中),则该代码很可能崩溃。


#13楼

从测试的角度来看,单例是更好的方法。 与静态类不同,单例可以实现接口,您可以使用模拟实例并将其注入。

在下面的示例中,我将对此进行说明。 假设您有一个isGoodPrice()方法,该方法使用getPrice()方法,并且将getPrice()作为单例方法实现。

提供getPrice功能的单例:

public class SupportedVersionSingelton {

    private static ICalculator instance = null;

    private SupportedVersionSingelton(){

    }

    public static ICalculator getInstance(){
        if(instance == null){
            instance = new SupportedVersionSingelton();
        }

        return instance;
    }

    @Override
    public int getPrice() {
        // calculate price logic here
        return 0;
    }
}

使用getPrice:

public class Advisor {

    public boolean isGoodDeal(){

        boolean isGoodDeal = false;
        ICalculator supportedVersion = SupportedVersionSingelton.getInstance();
        int price = supportedVersion.getPrice();

        // logic to determine if price is a good deal.
        if(price < 5){
            isGoodDeal = true;
        }

        return isGoodDeal;
    }
}


In case you would like to test the method isGoodPrice , with mocking the getPrice() method you could do it by:
Make your singleton implement an interface and inject it. 



  public interface ICalculator {
        int getPrice();
    }

最终的Singleton实施:

public class SupportedVersionSingelton implements ICalculator {

    private static ICalculator instance = null;

    private SupportedVersionSingelton(){

    }

    public static ICalculator getInstance(){
        if(instance == null){
            instance = new SupportedVersionSingelton();
        }

        return instance;
    }

    @Override
    public int getPrice() {
        return 0;
    }

    // for testing purpose
    public static void setInstance(ICalculator mockObject){
        if(instance != null ){
instance = mockObject;
    }

测试类别:

public class TestCalculation {

    class SupportedVersionDouble implements ICalculator{
        @Override
        public int getPrice() { 
            return 1;
        }   
    }
    @Before
    public void setUp() throws Exception {
        ICalculator supportedVersionDouble = new SupportedVersionDouble();
        SupportedVersionSingelton.setInstance(supportedVersionDouble);

    }

    @Test
    public void test() {
          Advisor advidor = new Advisor();
          boolean isGoodDeal = advidor.isGoodDeal();
          Assert.assertEquals(isGoodDeal, true);

    }

}

如果我们选择使用静态方法来实现getPrice(),则很难模拟getPrice()。 您可以使用电源模拟来模拟静态,但并非所有产品都可以使用它。


静态类

  • 具有所有静态方法的类。
  • 性能更好(静态方法在编译时绑定)
  • 不能覆盖方法,但可以使用方法隐藏。 ( Java中隐藏的方法是什么?甚至JavaDoc的解释也令人困惑 )
public class Animal { public static void foo() { System.out.println("Animal"); } } public class Cat extends Animal { public static void foo() { // hides Animal.foo() System.out.println("Cat"); } }

辛格尔顿

  • 只能实例化一次的对象
  • 可以覆盖方法( Java为什么不允许覆盖静态方法? )
  • 比静态方法更容易模拟
  • 更好地保持状态

总之,我只会使用静态类来保存util方法,而将Singleton用于其他所有内容。


编辑

  • 静态类也被延迟加载。 谢谢@jmoreno( 什么时候进行静态类初始化? )
  • 隐藏静态类的方法。 谢谢@MaxPeng。

#15楼

从客户端的角度来看,静态行为是客户端已知的,但是Singleton行为可以在客户端隐藏的情况下完成。 客户可能永远不会知道,他一次又一次地玩着一个实例。


#16楼

静态类:-

  1. 您不能创建静态类的实例。
  2. 加载包含类的程序或名称空间时,.NET Framework公共语言运行时(CLR)自动加载。
  3. 静态类不能具有构造函数。
  4. 我们不能将静态类传递给方法。
  5. 我们不能在C#中将Static类继承到另一个Static类。
  6. 具有所有静态方法的类。
  7. 性能更好(静态方法在编译时绑定)

单身人士:-

  1. 您可以创建该对象的一个实例并重新使用它。
  2. 当用户请求时,首次创建Singleton实例。
  3. Singleton类可以具有构造函数。
  4. 您可以创建单例类的对象并将其传递给方法。
  5. Singleton类未说明继承的任何限制。
  6. 我们可以处理单例类的对象,但不能处理静态类的对象。
  7. 方法可以被覆盖。
  8. 可以在需要时延迟加载(始终加载静态类)。
  9. 我们可以实现接口(静态类不能实现接口)。

#17楼

我阅读了以下内容,并认为这也很有意义:

照顾生意

请记住,最重要的OO规则之一是对象负责自身。 这意味着有关类生命周期的问题应在类中处理,而不是委托给诸如静态语言之类的语言构造。

摘自《面向对象的思维过程》第四版。


#18楼

我同意以下定义:

单个 ”一词是指整个应用程序生命周期中的单个对象,因此范围是在应用程序级别。

静态对象没有任何对象指针,因此作用域位于应用程序域级别。

此外,两者都应实现为线程安全的。

您可以找到有关以下方面的有趣差异: 单例模式与静态类


#19楼

  1. 我们可以创建单例类的对象并将其传递给方法。
  2. Singleton类没有任何继承限制。
  3. 我们不能处理静态类的对象,但可以单例类。

#20楼

正如我理解静态类和非静态Singleton类之间的区别,静态只是C#中的非实例化“类型”,其中Singleton是一个真正的“对象”。 换句话说,静态类中的所有静态成员都被分配给该类型,但是在Singleton中被置于该对象下。 但请记住,静态类仍然表现得像引用类型,因为它不是像Struct这样的值类型。

这意味着当你创建一个Singleton时,因为类本身不是静态的,但它的成员是,优点是Singleton中的静态成员引用它本身连接到一个实际的“对象”而不是它自身的空洞“类型”。 现在澄清了静态和非静态单例之间的区别,超出了它的其他特性和内存使用,这让我感到困惑。

两者都使用静态成员,它们是成员的单个副本,但Singleton将引用的成员包装在一个真实例化的“对象”周围,除了静态成员之外,该对象还存在地址。 该对象本身具有属性,其中in可以传递并引用,增加值。 Static类只是一个类型,因此除了指向其静态成员之外它不存在。 这种概念巩固了Singleton vs Static Class的目的,超越了继承和其他问题。


#21楼

static类不适用于需要状态的任何内容。 这对于将一堆函数(例如, Math (或项目中的Utils ))放在一起很有用。 因此,类名只是为我们提供了一个线索,在这里我们可以找到函数,仅此而已。

Singleton是我最喜欢的模式,我用它来单点管理某些事情。 它比static类更灵活,并且可以维护其状态。 它可以实现接口,从其他类继承并允许继承。

我在staticsingleton之间进行选择的规则:

如果有一堆应该保持在一起的功能,则选择static 。 需要对某些资源进行单一访问的任何其他内容都可以实现为singleton


#22楼

主要区别在于:

  • Singleton有一个实例/对象,而静态类是一堆静态方法
  • 单例可以通过接口扩展,而静态类则不能。
  • 可以继承Singleton,它支持SOLID原则中的打开/关闭原则,另一方面,静态类不能被继承,我们需要自己进行更改。
  • 单例对象可以传递给方法,而静态类因为没有实例而不能作为参数传递

#23楼

Java中的静态类只有静态方法。 它是功能的容器。 它是基于过程编程设计创建的。

Singleton类是面向对象设计中的一种模式。 Singleton类在JVM中只有一个对象的实例。 这种模式的实现方式是,JVM中始终仅存在该类的一个实例。


#24楼

实例化了Singleton,只是实例化了一个实例,因此Singleton中的单个实例。

静态类不能由其自身实例化。


#25楼

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

快速示例:

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

#26楼

是什么让您说单例或静态方法不是线程安全的? 通常,两者都应实现为线程安全的。

单例和一堆静态方法之间的最大区别在于,单例可以实现接口(或从有用的基类派生,尽管根据我的经验,这种情况不那么常见),因此您可以将单例当作“只是另一个实施。


#27楼

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

单例允许访问单个创建的实例-该实例(或对该实例的引用)可以作为参数传递给其他方法,并被视为普通对象。

静态类仅允许使用静态方法。


#28楼

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


#29楼

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

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


#30楼

好吧,单例只是被实例化的普通类,而只是从客户端代码中间接获得一次。 静态类未实例化。 据我所知,静态方法(静态类必须具有静态方法)比非静态方法要快。

编辑:
FxCop Performance规则说明:“不访问实例数据或调用实例方法的方法可以标记为静态(在VB中为Shared)。这样做之后,编译器将向这些成员发出非虚拟调用站点,这将阻止在确保当前对象指针为非空的每次调用的运行时。这可能会导致性能敏感代码的性能得到可衡量的提高。在某些情况下,无法访问当前对象实例表示正确性问题。”
我实际上不知道这是否也适用于静态类中的静态方法。


#31楼

单例的另一个优点是可以轻松地进行序列化,如果您需要将其状态保存到磁盘或远程将其发送到某个地方,则可能需要单序列化。