此文为译文, 原文在: https://javarevisited.blogspot.com/2013/03/difference-between-singleton-pattern-vs-static-class-java.html
译者注:
- 单例模式:是指实现了单例模式的类。下文提到单例类的时候指的是同一含义。
- 静态类: 此处的静态类并不是特指语法意义上的静态类,而是指只包含静态方法的类。下文提到的
静态类
或静态
或静态方法
,在本文中的含义是一样的。 - 另外一篇关于单例模式的不错的文章:http://poltora.info/blog/all-you-want-to-know-about-singleton/
单例模式和静态类都很容易理解, 他们之间有一些相似之处,比如: 他们都可以在不创建新对象的前提下使用,他们都只提供一个实例对象。大体上看, 他们好像都是为了同样的目的而存在。正是因为这种相似点,在面试的时候经常会被问到: 什么情况下需要用单例而不使用静态方法?
或者 你能用静态类(static class)来代替单例模式吗?他们之间有什么区别?
。为了回答这两个问题,了解单例模式和静态类之间的本质区别就很重要。单例模式提供给你一个对象,而静态类提供给你静态方法。因为你得到一个对象总比只提供给你一个方法要能做更多的事情,这一点能帮助你做出决定,什么时候应该用单例,而什么时候应该用静态方法。
通过这篇文章,你将会了解到什么情况下使用单例模式比较好,以及什么时候使用静态类会更好一点。在 JDK 中已经有一些关于单例模式和静态类的使用场景,而且他们使用的非常恰当。比如: java.lang.Math
就是一个只包含静态方法的 final class
; 而java.lang.Runtime
则是一个单例类的实现。如果你对单例模式或静态类不太熟悉,那么你需要知道的是 静态类是一个只能包含静态方法(而不能包含实例方法)的 Java 类, 一个比较好的例子就是java.lang.Math
, 在这个类当中包含了大量的关于数学计算的util方法, 比如sqrt()
。 而单例类是那些在整个应用程序中只存在一个实例的类,比如java.lang.Runtime
.
什么时候应该用静态类而不是单例
确实存在这样一些场景,用静态类比单例类更合适。一个不错的例子就是
java.lang.Math
他不是一个单例类, 而是一个全部方法都定义为静态方法的类。下面列出来的是一些我认为使用静态类比单例类更合适的场景。
-
如果你的单例类并没有保存任何状态性质的属性,而只是提供了一些公共方法,那么就应该考虑包含静态方法的类,因为 Java 在编译期的静态绑定特性,静态方法比单例类要快很多。但是请记住,在静态类中维护状态并不是一种推荐的做法,尤其是在并发执行的环境中,因为在多线程运行修改静态变量的状态时,如果不能正确地处理线程之间的关系,这可能会导致由于条件竞争而出现莫名其妙的问题。
-
另外,当你需要把一系列的 util 方法放到一个类当中时,应该用静态方法;除此之外,当你希望只对外提供一种资源访问的方式,那么就应该使用单例模式。
单例类和静态方法之间的区别
这是我们的第二个关于单例和静态之间关系的问题。我之前说过,他们之间最本质的区别就是,一个代表对象,而另一个代表方法。下面是 Java 当中二者之间的其他不同之处。
- 静态类比单例模式的效率更高,因为静态方法在编译期就完成了静态绑定。
- 另外一个区别就是,是否支持覆写(override)。因为 Java 中的静态方法是不能被覆写的,这就导致某些情况不够灵活。而你随时可以继承一个非 final 的单例类来覆写其中的方法。
- 在做单元测试的时候,静态类比单例类更难被 mock,因此也更难被测试。而单例类很容易被 mock 来执行单元测试。在使用 Junit 的时候,你可以更简单地为构造方法或普通方法传入单例类的 mock 对象来执行测试用例。
- 如果你需要维护一些状态信息,那么单例比静态类更合适。因为在静态类中维护状态信息很容易导致细微的 bug 。
- 单例对象可以被延迟初始化。而静态类总是在类被加载的时候就初始化。
- 很多依赖注入的框架很好的处理了单例对象,比如在 Spring 中实现一个单例对象就很简单。
以上就是关于静态类和单例类之间的区别。了解这些会有助于你决定在何时使用何种方式来完成你的工作。
相较于静态类,单例模式的优点有哪些
单例类最主要的优点就是,单例模式面向对象的特性更强一些。在使用单模式的时候,你可以通过继承来实现多态,也可以通过实现接口来提供同一接口的不同实现。 比如java.lang.Runtime
, 他就是一个单例类,当调用 getRuntime()
的时候,不同的 JVM 中可以返回不同的实现对象,同时又可以保证在 JVM 中只存在一份实例。如果我们把java.lang.Runtime
定义为一个静态类就不可能实现在不同 JVM 中返回不同实现的行为。
上面就是关于单例类和静态类的区别。当你需要一个具有面向对象特性的类的时候,就用单例模式;而如果你只是把一些静态方法放到一个类中,那就用静态类。
网友评论