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


1766

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

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


4
根据语言的实现方式和您的使用模式,由于每次使用该方法时都要调用该方法的开销,因此Singleton的效率可能较低getInstance()(尽管在大多数情况下这并不重要)。
太多php

5
已经有很多答案了。它实际上是方法只是函数的singleton对象,static是一个非OO实体。
fastcodejava 2011年

4
取决于实现方式。csharpindepth.com
Articles/General/

4
当您要允许第三方提供类的实现时,会有区别。在这种情况下,通常也需要使用Factory模式。参见agiletribe.wordpress.com/2013/10/08/…–
AgilePro

IMO这个答案很好地总结了stackoverflow.com/questions/14097656/…–
Dave

Answers:


1250

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

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


29
好吧,如果您愿意,两者都不是本质上是线程安全的,则必须使它们都是线程安全的,因此两者都没有区别。
豪尔赫·科尔多瓦

119
你能给的一些东西,一个例子固有的线程安全的,除了稳定的类型?
乔恩·斯基特

26
对于Skeet:人们说单例不是线程安全的,这意味着线程之间不必要地始终共享单例,而堆栈对象在需要时共享,这意味着您不必进行不必要的同步。

45
@Geek:想象一下单例实现一个interface Foo,并且您有一个将a Foo作为参数的方法。通过该设置,调用者可以选择使用单例作为实现,也可以使用其他实现。该方法与单例解耦。将其与类仅具有静态方法的情况进行比较-每个要调用这些方法的代码都与该类紧密耦合,因为它需要指定哪个类包含静态方法。
乔恩·斯基特

10
@AmirBareket:按照单例设计模式,它不是单例-如果类本身允许创建多个实例,则无论工厂做什么,它都不是单例IMO。
乔恩·斯基特

475

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

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

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


64
但是,如果可以通过调用静态getInstance()方法从几乎任何地方访问同一实例,为什么还要传递一个Singleton作为参数?
Henrique Ordine

23
@HenriqueOrdine那么它可以适合现有代码并提供接口吗?

6
@HenriqueOrdine他们在谈论静态类,而不是带有静态方法的类。静态类无法实例化。但是,如果传递包含静态方法的(非静态)类的实例,则无法在该实例上调用静态方法。
Goran 2012年

3
什么是静态类?至少在Java中,没有这样的东西。
Henrique Ordine 2012年

16
@Goran起初,我对您的措辞感到非常困惑。您说“您不能在实例上调用静态方法”。我读为“如果您引用了实例化对象,则无法调用它可能具有的任何静态方法”。那当然是不正确的。再次阅读几次后,我认为您的意思是“从静态方法内部无法访问类中的非静态对象”,这是正确的。想要澄清的是,对于那些不熟悉这些概念的人,只要遇到此答案并阅读您的评论即可。
Andrew Steitz

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

99
第二句话是错误的。我们无法克隆Singleton对象。单例实现必须拒绝这一点。如果您确实可以克隆Singleton,则不是Singleton。
亚历山大·延查鲁克

19
对于Java,这是不正确的答案:单例和静态都不使用堆栈。
AgilePro 2013年

72
#1不重要。#2描述了一个有缺陷的实现。#3是完全没有道理的。
Casey

31
静态对象如何存储在堆栈中?调用方法时会创建新的堆栈框架,它会存储方法的局部变量,方法返回时会删除此堆栈框架,并且这些局部变量会丢失。确定的堆栈速度很快,但是不适合存储静态对象。
mike_m 2014年

23
我无法理解这一点上的支持数。1)为什么必须将Singleton存储在堆栈中?在诸如C#或Java的托管语言中,数据存储在托管堆中,本地方法变量/参数除外。2)如果可以克隆它,则它不是正确实现的单例。3)Singleton被称为OOP反模式;也就是说,如果可能的话,你应该避免的事情。4)这是唯一正确的事情。
Groo

152

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


10
+1表示务实的观点。通常,单例模式被过度使用,但是在某些情况下它很合适。另请参阅: agiletribe.wordpress.com/2013/10/08/…–
AgilePro

3
您对多态性的优势是正确的。这是最重要的一点
Ahmad

嵌套的静态类可以实现接口。尝试编码,将起作用。我可以编译代码而没有任何错误。
nanosoft '16

75

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

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

我在static和之间选择的规则singleton

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


16
为什么静态类不做任何需要保存状态的事情?
2012年

12
@Trisped:您既不能精确控制初始化还是终结。
Xaqron

7
您因“ Singleton是我最喜欢的模式”而迷失了我。单例是一个尖锐的角,因此应将其视为反模式以及模式。如果静态比单例更“单次访问”,则类可以具有静态状态,也就是单次访问,​​因为大多数单例实现都被破坏了。您可以克隆单例,而静态会因定义而受益,因为它是唯一的。
PoweredByRice

1
维持状态意味着什么?什么是状态?
凯尔·德莱尼

2
@KyleDelaney:简单State来说,就是对象的不同属性的组合,这些属性通常会随着时间而变化。您可以对Google进行正式定义。
Xaqron

65

静态类别:-

  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. 我们可以实现接口(静态类不能实现接口)。


13
静态类确实具有构造函数:msdn.microsoft.com/en-us/library/k9x6w0hc.aspx
Tomer Arazy

2
是的,static可以具有该类内部的构造函数。调用类中的任何静态方法时,将调用此方法。
rahulmr

对于编译时的单例,它存储在HEAP内存中,但是如果实例化一次,是否将其存储在STACK中?
Luminous_Dev

@Luminous_Dev否。任何单例实例都是一天结束时的对象实例。毫无疑问,它将被存储在堆中。
RBT

1
@rahulmr重要的区别:在创建第一个(仅AKA)实例之前,构造函数也会被调用。
CoolOppo

53

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

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


1
多态性根本不会与单例一起发挥作用

32
所以你认为。我有不同的看法。;)例如,假设有一个返回接口的单例工厂。您知道您正在获得一个ISingleton(并且永远是同一个人),但不一定是哪种实现。
Morendil

嵌套的静态类也可以具有实例方法,它不限于仅具有静态方法。将其编码即可看到。
nanosoft '16

在对象模型更好的语言(例如Ruby)中,类也是对象。静态类的“纯过程”方面是该语言施加的任意限制。
最多

36

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

快速示例:

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

39
这并不是真正的单例模式,对我来说更像是工厂。
vava

10
两者之间的根本区别并不完全是,Singleton将“缓存”其单个对象并保持返回(引用)相同的对象。Factory模式将创建新实例。
Mystic

12
然后是proxy-singleton :)
vava

3
嗯,我知道这种Singleton是MonoState。
嬉皮

例如工厂模式
Rajavel D

26

扩展Jon Skeet的答案

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

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


我认为您不能直接嘲笑单身人士。您是否不必声明单例和模拟类都实现的接口?
Ellen Spertus 2014年

@espertus为什么您不能嘲笑您的单身人士?使用嘲笑的例子MySingleton mockOfMySingleton = mock(MySingleton.class)
Mike Rylander 2014年

没错,您可以使用像反射这样的工具来模拟它。我的意思是,不能通过对其进行子类化和覆盖其方法来直接模拟它。
Ellen Spertus

@espertus为什么不呢?实例化要测试的对象时,无论您使用原始对象的哪个位置,都可以替换其单例的子类实现。例如:new ClazzToTest(mockSingleton);
Mike Rylander 2014年

我还没有使用过Mockito,但是如何将具有私有构造函数的类子类化(单例除外),除非使用反射?相关讨论:stackoverflow.com/questions/2302179/mocking-a-singleton-class stackoverflow.com/questions/15939023/...
艾伦斯珀特斯

23

这是一篇好文章:http : //javarevisited.blogspot.com.au/2013/03/difference-between-singleton-pattern-vs-static-class-java.html

静态类

  • 具有所有静态方法的类。
  • 性能更好(静态方法在编译时绑定)
  • 不能覆盖方法,但可以使用方法隐藏。(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");
        }
    }
    

辛格尔顿

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


编辑


4
我不了解Java,但是在.Net中,您的最后两点是不正确的。静态类可以引用静态属性和字段,因此在状态上它们是相等的。并且它们是延迟加载的-静态构造函数在以下情况下运行:1)创建该类的实例。2)引用了该类的任何静态成员。1不适用,剩下2个。因此,直到第一次使用静态类时,才会加载静态类。
jmoreno

1
对于静态类,尽管不能覆盖static方法,但是可以从其父级隐藏static方法。
马克斯·彭

如果Animal animal = new Cat();animal.foo();会发生什么呢?
Luminous_Dev

@jmoreno静态类直到第一次使用才加载?我相信它是在编译时存储在堆栈内存中的。并且可以立即访问..不是吗?
Luminous_Dev

@Luminous_Dev:至少对于.net而言,静态类具有一个构造函数,该构造函数在首次访问时运行,因此不可以立即访问。理论上,静态构造函数可能会花费无穷的时间。它(或存储任何其他类)的位置是一个实现细节,与该问题并没有真正的关系。
jmoreno

22

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


19

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

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

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

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


15
是的,其他所有人似乎都忽略了一个事实,即带有静态方法的类也可以具有私有静态字段,该字段仍可以用来维护状态(并通过公共静态设置器/获取器将其中一些公开给客户端代码)。
user289463

17

为了说明乔恩的观点,如果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();
    }
}

13

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

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


11

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

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


静态类可以在java中实例化很多。阅读docs.oracle.com/javase/tutorial/java/javaOO/nested.html。另请参阅我的答案stackoverflow.com/a/37114702/1406510
nanosoft '16

8

主要区别在于:

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

7

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

在下面的示例中,我将对此进行说明。假设您有一个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()。您可以使用电源模拟来模拟静态,但并非所有产品都可以使用它。


1
现在这不是线程安全的,并且在访问接口实现方面通常令人讨厌。当然,拥有一个界面可测试性很好-但是,为什么还要麻烦一个人呢?只是要避免单身。有一类出于生产目的实现它,一类出于测试目的实现,并根据您的工作注入正确的实例。完全不需要将单例与其调用者耦合。
乔恩·斯基特

感谢您的反馈。使线程安全非常简单。另外,我使用单例进行缓存。
Amir Bareket

1
是的,尽管没有任何意义的开销。同样,不使用单例会更简单。
乔恩·斯基特

6

我同意以下定义:

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

没有任何对象的指针,因此范围是在应用程序域级别。

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

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


5

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

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


4

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

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

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


4

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

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

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

希望对您有帮助。


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

嵌套的静态类可以在Java中实现很多接口。您的第二点是错误的。
nanosoft '16

3

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

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

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


3

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


3

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

照顾生意

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

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


我不同意,因为这实际上只是给班级增加了责任,这(假设它做了任何事情)意味着它现在违反了单一责任原则。
ssmith

3

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

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

我会重新整理一下英语语法,但除此之外,这是一个有趣的读物:)
Noctis 2014年

3
  1. 我们可以创建单例类的对象并将其传递给方法。

  2. Singleton类没有任何继承限制。

  3. 我们不能处理静态类的对象,但可以单例处理类。


如果总是只有一个并且总是有静态引用,那么将单例传递给方法有什么用?
亚伦·弗兰克

3

与静态类的区别

JDK有单例和静态的示例,一方面java.lang.Math是带有静态方法的最终类,另一方面java.lang.Runtime是单例类。

单身人士的优势

  • 如果您需要维护状态而不是单例模式比静态类更好,因为在静态类中维护状态会导致错误,尤其是在并发环境中,这可能导致竞争状况,而没有足够的同步并行修改多个线程。

  • 如果Singleton类是一个沉重的对象,则可以将其延迟加载,但是静态类没有这种优势,并且总是急于加载。

  • 使用单例,您可以使用继承和多态性来扩展基类,实现接口并提供不同的实现。

  • 由于Java中的静态方法不能被覆盖,因此会导致不灵活。另一方面,您可以通过扩展它覆盖单例类中定义的方法。

静态类的缺点

  • 为单例编写单元测试比静态类要容易,因为只要需要单例,就可以传递模拟对象。

静态类的优点

  • 静态类比单例提供更好的性能,因为静态方法是在编译时绑定的。

单例模式有几种实现,每种都有优点和缺点。

  • 渴望加载单身人士
  • 双重检查锁定单身人士
  • 按需初始化持有人惯用语
  • 基于枚举的单例

详细说明每个都太冗长,因此我只链接了一篇好文章- 您想了解的有关Singleton的全部信息


2

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

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


1
因此,如果应用程序退出,Singleton仍会保留在内存中吗?
nanosoft '16

我认为您的意思是退出当前线程而不是退出应用程序,对吗?如果应用程序退出,则其他线程无法使用其中的任何内容。
汤姆·布里托

2

我脑子里的不同是实现面向对象的编程(Singleton / Prototype)或功能编程(静态)。

当我们最后要保留一个对象时,我们太关注单例模式创建的对象数。就像其他人已经说过的那样,它可以扩展,作为参数传递,但最重要的是它是全状态的。

另一方面,static用于实现功能编程。静态成员属于一个类。他们是无国籍的。

顺便说一句,您知道可以创建单例静态类:)


由于将单例作为参数传递,这有什么意义呢?
亚伦弗兰克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.