静态类和单例模式之间存在什么实际的区别?

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


当前回答

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

打理业务记住,最重要的OO规则之一是对象对自己负责。这意味着关于类的生命周期的问题应该在类中处理,而不是委托给静态等语言构造。

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

其他回答

我同意这个定义:

单词“single”表示整个应用程序生命周期中的单个对象循环,因此范围在应用程序级别。静态不具有任何Object指针,因此作用域位于AppDomain级别。此外,两者都应实现为线程安全。

你可以发现其他有趣的区别:Singleton模式与静态类

单个对象存储在堆中,但静态对象存储在堆栈中。我们可以克隆(如果设计器不允许)单例对象,但不能克隆静态类对象.单类遵循OOP(面向对象原则),静态类则不遵循。我们可以用Singleton类实现接口,但类的静态方法(例如C#静态类)不能。

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

静态类实际上不是规范类——它是一个包含函数和变量的命名空间由于打破了面向对象的编程原则,使用静态类不是一种好的做法静态类不能作为其他类的参数传递静态类不适合“惰性”初始化静态类的初始化和使用始终是硬跟踪的实现线程管理很困难

在许多情况下,这两者没有实际差异,特别是如果单例实例从未更改或更改非常缓慢,例如保持配置。

我想说,最大的区别是单例仍然是一个普通的JavaBean,而不是专门的仅静态Java类。正因为如此,单身汉在很多情况下都被接受;它实际上是默认的Spring框架的实例化策略。消费者可能知道也可能不知道它是一个被传递的单例,它只是把它当作一个普通的Javabean。如果需求发生变化,而单例需要成为原型,正如我们在Spring中经常看到的那样,它可以完全无缝地完成,而无需对消费者进行一行代码更改。

前面有人提到,静态类应该是纯过程的,例如java.lang.Math。在我看来,这样的类永远不应该被传递,它们永远不应该将静态final以外的任何属性作为属性。对于其他一切,使用单例,因为它更灵活,更易于维护。

从测试角度来看,Singleton是更好的方法。与静态类不同,singleton可以实现接口,您可以使用mock实例并注入它们。

在下面的示例中,我将对此进行说明。假设您有一个使用getPrice()方法的方法isGoodPrice(。

提供getPrice功能的singleton:

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)。您可以使用power mock模拟静态,但并非所有产品都可以使用它。