静态类和单例模式之间存在什么真正的(即实际的)区别?
两者都可以在没有实例化的情况下调用,都只提供一个“实例”,而且它们都不是线程安全的。还有其他区别吗?
静态类和单例模式之间存在什么真正的(即实际的)区别?
两者都可以在没有实例化的情况下调用,都只提供一个“实例”,而且它们都不是线程安全的。还有其他区别吗?
是什么让您说单例或静态方法不是线程安全的?通常两者都应该实现为线程安全的。
单例和一堆静态方法之间的最大区别在于单例可以实现接口(或从有用的基类派生,尽管根据我的经验这不太常见),因此您可以传递单例,就好像它“只是另一个“ 执行。
真正的答案是 Jon Skeet在另一个论坛上的。
单例允许访问单个创建的实例——该实例(或者更确切地说,对该实例的引用)可以作为参数传递给其他方法,并被视为普通对象。
静态类只允许静态方法。
interface
使用 Singleton 类实现 an,但类的静态方法(或例如 C# static class
)不能。单例模式与静态类相比有几个优点。首先,单例可以扩展类和实现接口,而静态类不能(它可以扩展类,但不继承它们的实例成员)。单例可以延迟或异步初始化,而静态类通常在首次加载时进行初始化,从而导致潜在的类加载器问题。然而,最重要的优点是可以多态地处理单例,而不会强迫用户假设只有一个实例。
static
类不适合任何需要状态的东西。Math
将一堆功能放在一起(或Utils
在项目中)很有用。所以类名只是给了我们一个线索,我们可以在哪里找到函数,仅此而已。
Singleton
是我最喜欢的模式,我用它来管理某件事。它比static
类更灵活,并且可以维护它的状态。它可以实现接口,从其他类继承并允许继承。
static
我在和之间选择的规则singleton
:
如果有一堆功能应该放在一起,那static
就是选择。任何其他需要单一访问某些资源的东西,都可以实现为singleton
.
静态类:-
您不能创建静态类的实例。
加载包含该类的程序或命名空间时,由 .NET Framework 公共语言运行时 (CLR) 自动加载。
我们不能将静态类传递给方法。
我们不能将静态类继承到 C# 中的另一个静态类。
具有所有静态方法的类。
更好的性能(静态方法在编译时绑定)
单身人士:-
您可以创建对象的一个实例并重复使用它。
Singleton 实例在用户请求时首次创建。
您可以创建单例类的对象并将其传递给方法。
单例类没有说任何继承限制。
我们可以处理单例类的对象,但不能处理静态类的对象。
方法可以被覆盖。
需要时可以延迟加载(总是加载静态类)。
我们可以实现接口(静态类不能实现接口)。
静态类是只有静态方法的类,更好的词是“函数”。静态类中体现的设计风格是纯程序化的。
另一方面,Singleton 是一种特定于 OO 设计的模式。它是一个对象的实例(具有其中固有的所有可能性,例如多态性),具有一个创建过程,可确保该特定角色在其整个生命周期内只有一个实例。
在单例模式中,您可以将单例创建为派生类型的实例,而使用静态类则无法做到这一点。
快速示例:
if( useD3D )
IRenderer::instance = new D3DRenderer
else
IRenderer::instance = new OpenGLRenderer
单例和一堆静态方法之间的最大区别在于,单例可以实现接口(或派生自有用的基类,尽管这不太常见 IME),因此您可以传递单例,就好像它是“只是另一个”实现一样。
在对类进行单元测试时,单例更容易使用。无论您在何处将单例作为参数(构造函数、设置器或方法)传递,您都可以替换为单例的模拟或存根版本。
这是一篇好文章: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 用于其他所有内容。
编辑
静态类也是延迟加载的。谢谢@jmoreno(静态类初始化什么时候发生?)
静态类的方法隐藏。谢谢@MaxPeng。
单例的另一个优点是它可以很容易地序列化,如果您需要将其状态保存到磁盘或将其远程发送到某个地方,这可能是必要的。
我不是一个伟大的 OO 理论家,但据我所知,我认为与 Singleton 相比,静态类唯一缺乏的 OO 特性是多态性。但是如果你不需要它,你当然可以使用静态类进行继承(不确定接口实现)以及数据和函数封装。
Morendil 的评论,“体现在静态类中的设计风格纯粹是程序性的”我可能是错的,但我不同意。在静态方法中,您可以访问静态成员,这与访问其单个实例成员的单例方法完全相同。
编辑:
我现在实际上在想,另一个区别是静态类在程序启动时实例化*并存在于程序的整个生命周期中,而单例在某些时候被显式实例化并且也可以被销毁。
* 或者它可能在第一次使用时被实例化,这取决于语言,我认为。
为了说明 Jon 的观点,如果 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();
}
}
好吧,单例只是一个被实例化的普通类,但只从客户端代码间接地实例化一次。静态类未实例化。据我所知静态方法(静态类必须有静态方法)比非静态更快。
编辑:
FxCop 性能规则描述:“不访问实例数据或调用实例方法的方法可以标记为静态(在 VB 中共享)。这样做之后,编译器将向这些成员发出非虚拟调用站点,这将阻止在运行时检查确保当前对象指针不为空的每个调用。这可以为性能敏感代码带来可衡量的性能提升。在某些情况下,无法访问当前对象实例表示正确性问题。
我实际上不知道这是否也适用于静态类中的静态方法。
单例是实例化的,只是只有一个实例被实例化,因此单例中的单例。
静态类不能被它自己以外的任何东西实例化。
主要区别在于:
JDK有单例和静态的例子,一方面java.lang.Math
是带有静态方法的final类,另一方面java.lang.Runtime
是单例类。
单例的优点
如果您需要维护状态而不是单例模式比静态类更好的选择,因为在静态类中维护状态会导致错误,尤其是在并发环境中,这可能会导致竞争条件,而没有足够的同步并行修改多个线程。
如果单例类是重对象,则可以延迟加载,但静态类没有这样的优势,并且总是急切地加载。
使用单例,您可以使用继承和多态来扩展基类、实现接口并提供不同的实现。
由于 Java 中的静态方法不能被覆盖,它们会导致不灵活。另一方面,您可以通过扩展来覆盖单例类中定义的方法。
静态类的缺点
静态类的优点
每一个的详细描述都太冗长了,所以我只是放了一篇好文章的链接——关于 Singleton 你想知道的一切
从测试的角度来看,单例是更好的方法。与静态类不同,单例可以实现接口,您可以使用模拟实例并注入它们。
在下面的示例中,我将说明这一点。假设您有一个 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();
}
最终单例实现:
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 来模拟静态,但并非所有产品都可以使用它。
我同意这个定义:
“单一”一词是指整个应用程序生命周期中的单个对象,因此范围是应用程序级别。
静态没有任何对象指针,因此范围在应用程序域级别。
此外,两者都应该实现为线程安全的。
您可以找到其他有趣的差异:单例模式与静态类
一个显着的区别是 Singleton 附带的不同实例化。
对于静态类,它由 CLR 创建,我们无法控制它。使用单例,对象在第一个尝试访问的实例上被实例化。
在许多情况下,这两者没有实际区别,特别是如果单例实例从不更改或更改非常缓慢,例如保持配置。
我想说最大的区别是单例仍然是普通的 Java Bean,而不是专门的仅静态 Java 类。正因为如此,单例在更多情况下被接受;它实际上是默认 Spring Framework 的实例化策略。消费者可能知道也可能不知道它是一个被传递的单例,它只是把它当作一个普通的 Java bean。如果需求发生变化,而单例需要变成原型,就像我们在 Spring 中经常看到的那样,它可以完全无缝地完成,而无需对消费者进行一行代码更改。
前面有人提到静态类应该是纯过程的,例如 java.lang.Math。在我看来,这样的类永远不应该被传递,它们不应该持有静态 final 作为属性以外的任何东西。对于其他一切,请使用单例,因为它更灵活且更易于维护。
我们有我们的数据库框架,可以连接到后端。为了避免跨多个用户的脏读,我们使用单例模式来确保我们在任何时间点都有可用的单个实例。
在 C# 中,静态类不能实现接口。当单个实例类需要为业务合同或 IoC 目的实现接口时,这就是我使用单例模式而不使用静态类的地方
Singleton 提供了一种在无状态场景中维护状态的方法
希望对你有帮助。。
在我写的一篇文章中,我描述了我对为什么单例比静态类好得多的观点:
单例类在应用程序生命周期中提供一个对象(只有一个实例),例如java.lang.Runtime
而静态类只提供静态方法如java.lang.Math
Java 中的静态方法不能被覆盖,但是 Singleton 类中定义的方法可以通过扩展来覆盖。
单例类能够继承和多态来扩展基类,实现接口并能够提供不同的实现。而静态不是。
例如:java.lang.Runtime
, 是 Java 中的 Singleton 类,调用getRuntime()
方法返回与当前 Java 应用程序关联的运行时对象,但确保每个 JVM 仅一个实例。
一种。序列化 - 静态成员属于该类,因此不能被序列化。
湾。尽管我们已经将构造函数设为私有,静态成员变量仍然会被携带到子类中。
C。我们不能进行延迟初始化,因为所有内容都只会在类加载时加载。
从客户端的角度来看,静态行为对客户端是已知的,但单例行为可以对客户端隐藏。客户可能永远不会知道只有一个实例,他一次又一次地玩弄。
我阅读了以下内容,并认为这也很有意义:
照顾生意
请记住,最重要的 OO 规则之一是对象对自己负责。这意味着关于类生命周期的问题应该在类中处理,而不是委托给静态等语言结构。
来自《面向对象的思维过程》第 4 版一书。
我们可以创建单例类的对象并将其传递给方法。
单例类没有任何继承限制。
我们不能处理静态类的对象,但可以单例类。
Java 中的静态类只有静态方法。它是功能的容器。它是基于过程编程设计创建的。
单例类是面向对象设计中的一种模式。Singleton 类在 JVM 中只有一个对象实例。这种模式以这样一种方式实现,即在 JVM 中始终只存在该类的一个实例。
单个静态类实例(即一个类的单个实例,恰好是静态或全局变量)和指向堆上类实例的单个静态指针之间存在巨大差异:
当您的应用程序退出时,将调用静态类实例的析构函数。这意味着如果您将该静态实例用作单例,您的单例将停止正常工作。如果仍有使用该单例的代码在运行,例如在不同的线程中,则该代码可能会崩溃。
我脑子里的区别是实现面向对象的编程(单例/原型)或函数式编程(静态)。
我们过于关注单例模式创建的对象的数量,而我们应该关注的是最终我们持有一个对象。就像其他人已经说过的那样,它可以扩展,作为参数传递,但最重要的是它是状态完整的。
另一方面,静态用于实现函数式编程。静态成员属于一个类。他们是无国籍的。
顺便说一句,您知道您可以创建单例静态类 :)
当我想要具有完整功能的类时,例如有很多方法和变量,我使用单例;
如果我想要一个只有一个或两个方法的类,例如 MailService 类,它只有一个方法 SendMail() 我使用静态类和方法。
据我了解静态类和非静态单例类之间的区别,静态只是 C# 中的非实例化“类型”,其中单例是真正的“对象”。换句话说,静态类中的所有静态成员都分配给该类型,但在 Singleton 中却位于对象之下。但请记住,静态类的行为仍然像引用类型,因为它不是像 Struct 这样的值类型。
这意味着当你创建一个单例时,因为类本身不是静态的,但它的成员是,优点是单例中引用自身的静态成员连接到实际的“对象”而不是自身的空心“类型”。现在,除了其他功能和内存使用情况之外,这种方式澄清了静态和非静态单例之间的区别,这让我感到困惑。
两者都使用作为成员的单个副本的静态成员,但 Singleton 将引用的成员包装在一个真正的实例化“对象”周围,该对象的地址除了其静态成员之外还存在。该对象本身具有可以传递和引用的属性,从而增加了价值。Static 类只是一种类型,因此除了指向其静态成员之外它不存在。这个概念在某种程度上巩固了单例与静态类的目的,超越了继承和其他问题。
单例只不过是类上的一次写入静态变量,一旦初始化,它总是引用自身的同一个实例。
因此,您不能“使用单例代替静态变量”,也不能通过使用单例来避免将状态保留在静态变量中。
单例的优点仅在于:即使其他代码尝试重新初始化它一千次,它也不会重新初始化。这对于像网络处理程序这样的东西来说非常棒,你知道,如果在等待响应的过程中一个实例被另一个实例替换,它会很糟糕。
除非你想要一个在任何地方都没有实例的整个应用程序——全是静态的!——那么单例对于这些我们不能依赖缺乏人为错误作为唯一保证不会被覆盖的情况是有意义的。
但请注意,单身并不能保证在任何地方都生活在国家之中。您的网络处理程序本身也可能依赖于其他单例等。现在我们有状态生活在许多单例中......太棒了。
并且没有编译器可以确保在编译时单例是所有状态或任何其他此类想法所在的位置。您可以在具有单例的类上拥有一百个静态变量。并且单例可以访问静态变量。而且编译器不会在意。
所以我会提醒任何人不要假设使用单例可以保证任何关于状态所在的位置。它唯一的保证就是它永远是它的类的唯一实例。而这也是它唯一的优势。
其他答案声称的单例的任何其他优势都是编译器无法保证的,并且可能因语言而异。依赖注入是一种可能依赖单例的补充模式,尽管它可能是也可能不是给定语言中的最佳解决方案或唯一解决方案。在缺乏泛型的语言中,或者对调用静态访问器和函数施加任意限制的语言中,诉诸单例模式确实可能是解决给定问题的最佳可用解决方案。
在 Swift 之类的语言中,Singleton 根本不需要获得依赖注入、可测试的代码、良好管理的状态、线程安全的访问器、多态性等。但是它对于保证单个实例仍然有用。
回顾一下:单例只不过是一个静态变量,它可以防止存在给定类的多个实例,以及防止单个实例被新实例覆盖。就是这样,句号,句号。
带有静态类的示例
public class Any {
private static Any instance = new Any();
private Singleton() {
System.out.println("creating");
}
}
单例模式只存在一个实例:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
System.out.println("creating");
if (instance != null) {
throw new RuntimeException("Imposible create a new instance ");
}
}
}
静态类通常用于库,如果我只需要特定类的一个实例,则使用单例。从内存的角度来看,虽然存在一些差异:通常在堆中只分配对象,分配的唯一方法是当前正在运行的方法。静态类也具有所有静态方法,并且从一开始就在堆中,因此通常静态类会消耗更多内存。
单例的一个主要优点:多态例如:使用类工厂创建实例(基于某些配置),我们希望这个对象是真正的单例。
两者都可以在没有实例化的情况下调用,都只提供一个“实例”,而且它们都不是线程安全的。还有其他区别吗?
这个问题是错误的,它的两个陈述。请注意:这里的静态类是指嵌套的静态类而不是只有静态方法的类。
我假设(即静态类意味着嵌套的静态类,而不是只有静态成员的类),因为如果我看到最流行的单例实现,即 DCL 方式,它只不过是实例的静态声明和获取单例实例的静态方法。它的一种实现。所以在这种情况下,单例和只有静态成员的类有什么区别。尽管使用 Enum 可以实现其他实现。
让我更正以下陈述:
单例类可以在应用程序范围内具有单实例。嵌套的静态类可以有多个实例(参见下面的代码作为证明)。在此处阅读嵌套类的基础知识。
没有一个类本质上是线程安全的,它必须以编程方式使其成为线程安全的。它可以用于嵌套静态类和单例。
下面还有一些神话破坏者(这个问题的大多数答案都给出了这些陈述,因此认为以编程方式证明它会很好):
在下面的代码中,您可以看到嵌套静态类NestedStaticClass实现了接口,扩展了另一个类,具有实例变量和参数化构造函数。
package com.demo.core;
public class NestedStaticClassTest
{
public static void main(String[] args)
{
OuterClass.NestedStaticClass obj1 = new OuterClass.NestedStaticClass();
OuterClass.NestedStaticClass obj2 = new OuterClass.NestedStaticClass();
if(obj1 == obj2)
{
System.out.println("Both nested static objects are equal....");
}
else
{
System.out.println("NOT EQUAL......");
}
System.out.println(OuterClass.NestedStaticClass.d);
obj1.setD(5);
System.out.println(OuterClass.NestedStaticClass.d);
System.out.println(obj1.sum());
}
}
class OuterClass
{
int a =1;
static int b = 2;
static class NestedStaticClass extends OneClass implements Sample
{
int c = 3;
static int d = 4;
public NestedStaticClass()
{
}
//Parameterized constructor
public NestedStaticClass(int z)
{
c = z;
}
public int sum()
{
int sum = 0;
sum = b + c + d + getE();
return sum;
}
public static int staticSum()
{
int sum = 0;
sum = b + d;
return sum;
}
public int getC()
{
return c;
}
public void setC(int c)
{
this.c = c;
}
public static int getD()
{
return d;
}
public static void setD(int d)
{
NestedStaticClass.d = d;
}
}
}
interface Sample
{
}
class OneClass
{
int e = 10;
static int f = 11;
public int getE()
{
return e;
}
public void setE(int e)
{
this.e = e;
}
public static int getF()
{
return f;
}
public static void setF(int f)
{
OneClass.f = f;
}
}
我将尝试超越 WTMI 和 WTL;DR 响应。
单例是对象的一个实例......句号
您的问题从根本上是在询问一个类与该类的实例之间的区别。我认为这很清楚,不需要详细说明。
单例类一般会采取措施保证一个实例的构造;这很聪明,但不是必需的。
示例: var connection = Connection.Instance;
假设这是 Connection 类:
public sealed class Connection
{
static readonly Connection _instance = new Connection();
private Connection()
{
}
public static Connection Instance
{
get
{
return _instance;
}
}
}
请注意,您可以在该类上抛出一个接口并对其进行模拟以用于测试目的,而静态类无法轻松做到这一点。