静态类和单例模式之间存在什么真正(即实用)的区别?
两者都可以不实例化地调用,都只提供一个“实例”,并且它们都不是线程安全的。 还有其他区别吗?
#1楼
- 延迟加载
- 支持接口,因此可以提供单独的实现
- 能够返回派生类型(作为延迟加载和接口实现的组合)
#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楼
- 辛格尔顿对象存储在堆 ,但静态对象存储在堆栈中 。
- 我们可以克隆 (如果设计者不允许)单例对象,但是我们不能克隆静态类对象。
- 单例类遵循OOP (面向对象原则),而静态类则不遵循。
- 我们可以使用Singleton类实现
interface
,但是不能使用类的静态方法(例如C#static class
)。
#7楼
我不是一个很棒的面向对象理论家,但是据我所知,我认为静态类与Singletons相比唯一缺乏的OO特性是多态性。 但是,如果您不需要它,则使用静态类当然可以具有继承(不确定接口实现)以及数据和函数封装。
Morendil的评论“静态类中体现的设计风格纯粹是程序性的”,我可能是错的,但我不同意。 在静态方法中,您可以访问静态成员,这与访问其单个实例成员的单例方法完全相同。
编辑:
我现在实际上在想的另一个区别是,静态类在程序启动时实例化 ,并且在程序的整个生命周期中都存在,而单例则在某个时刻显式实例化,并且也可以销毁。
*或者我认为它可能会在首次使用时实例化,具体取决于语言。
#8楼
单例和一堆静态方法之间的最大区别在于,单例可以实现接口(或从有用的基类派生,尽管这是不太常见的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楼
在我写的一篇文章中,我描述了关于单例为什么比静态类好得多的观点:
- 静态类实际上不是规范类,它是具有函数和变量的名称空间
- 使用静态类不是一个好习惯,因为它违反了面向对象的编程原则
- 静态类不能作为其他类的参数传递
- 静态类不适用于“延迟”初始化
- 始终很难跟踪静态类的初始化和使用
- 实施线程管理很困难
#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()。 您可以使用电源模拟来模拟静态,但并非所有产品都可以使用它。
#14楼
这是一篇好文章: 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"); } }
辛格尔顿
- 只能实例化一次的对象 。
- 可以覆盖方法( Java为什么不允许覆盖静态方法? )
- 比静态方法更容易模拟
- 更好地保持状态
总之,我只会使用静态类来保存util方法,而将Singleton用于其他所有内容。
编辑
静态类也被延迟加载。 谢谢@jmoreno( 什么时候进行静态类初始化? )
隐藏静态类的方法。 谢谢@MaxPeng。
#15楼
从客户端的角度来看,静态行为是客户端已知的,但是Singleton行为可以在客户端隐藏的情况下完成。 客户可能永远不会知道,他一次又一次地玩着一个实例。
#16楼
静态类:-
您不能创建静态类的实例。
加载包含类的程序或名称空间时,.NET Framework公共语言运行时(CLR)自动加载。
静态类不能具有构造函数。
我们不能将静态类传递给方法。
我们不能在C#中将Static类继承到另一个Static类。
具有所有静态方法的类。
性能更好(静态方法在编译时绑定)
单身人士:-
您可以创建该对象的一个实例并重新使用它。
当用户请求时,首次创建Singleton实例。
Singleton类可以具有构造函数。
您可以创建单例类的对象并将其传递给方法。
Singleton类未说明继承的任何限制。
我们可以处理单例类的对象,但不能处理静态类的对象。
方法可以被覆盖。
可以在需要时延迟加载(始终加载静态类)。
我们可以实现接口(静态类不能实现接口)。
#17楼
我阅读了以下内容,并认为这也很有意义:
照顾生意
请记住,最重要的OO规则之一是对象负责自身。 这意味着有关类生命周期的问题应在类中处理,而不是委托给诸如静态语言之类的语言构造。
摘自《面向对象的思维过程》第四版。
#18楼
我同意以下定义:
“ 单个 ”一词是指整个应用程序生命周期中的单个对象,因此范围是在应用程序级别。
静态对象没有任何对象指针,因此作用域位于应用程序域级别。
此外,两者都应实现为线程安全的。
您可以找到有关以下方面的有趣差异: 单例模式与静态类
#19楼
我们可以创建单例类的对象并将其传递给方法。
Singleton类没有任何继承限制。
我们不能处理静态类的对象,但可以单例类。
#20楼
正如我理解静态类和非静态Singleton类之间的区别,静态只是C#中的非实例化“类型”,其中Singleton是一个真正的“对象”。 换句话说,静态类中的所有静态成员都被分配给该类型,但是在Singleton中被置于该对象下。 但请记住,静态类仍然表现得像引用类型,因为它不是像Struct这样的值类型。
这意味着当你创建一个Singleton时,因为类本身不是静态的,但它的成员是,优点是Singleton中的静态成员引用它本身连接到一个实际的“对象”而不是它自身的空洞“类型”。 现在澄清了静态和非静态单例之间的区别,超出了它的其他特性和内存使用,这让我感到困惑。
两者都使用静态成员,它们是成员的单个副本,但Singleton将引用的成员包装在一个真实例化的“对象”周围,除了静态成员之外,该对象还存在地址。 该对象本身具有属性,其中in可以传递并引用,增加值。 Static类只是一个类型,因此除了指向其静态成员之外它不存在。 这种概念巩固了Singleton vs Static Class的目的,超越了继承和其他问题。
#21楼
static
类不适用于需要状态的任何内容。 这对于将一堆函数(例如, Math
(或项目中的Utils
))放在一起很有用。 因此,类名只是为我们提供了一个线索,在这里我们可以找到函数,仅此而已。
Singleton
是我最喜欢的模式,我用它来单点管理某些事情。 它比static
类更灵活,并且可以维护其状态。 它可以实现接口,从其他类继承并允许继承。
我在static
和singleton
之间进行选择的规则:
如果有一堆应该保持在一起的功能,则选择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楼
单例的另一个优点是可以轻松地进行序列化,如果您需要将其状态保存到磁盘或远程将其发送到某个地方,则可能需要单序列化。