为什么静态变量被视为邪恶?

时间:2011-08-11 13:14:37

标签: java static

我是一名新的企业界Java程序员。最近我使用Groovy和Java开发了一个应用程序。我编写的所有代码都使用了相当多的静态代码。高级技术部门要求我减少使用的静力学数量。我用谷歌搜索了同样的东西,我发现很多程序员都反对使用静态变量。

我发现静态变量使用起来更方便。而且我认为它们也很有效(如果我错了,请纠正我),因为如果我必须对一个类中的函数进行10,000次调用,我很乐意将该方法设置为静态并使用简单的{{1}在它上面而不是用10,000个类的实例来混乱内存,对吗?

此外,静态减少了代码其他部分的相互依赖性。他们可以充当完美的国家持有者。除此之外,我发现静态广泛应用于某些语言,如SmalltalkScala。那么为什么这种对静态的压迫在程序员中普遍存在(特别是在Java世界中)?

PS:如果我对静力学的假设是错误的,请纠正我。

30 个答案:

答案 0 :(得分:644)

静态变量代表全局状态。这很难推理并且很难测试:如果我创建一个对象的新实例,我可以在测试中推断它的新状态。如果我使用的是使用静态变量的代码,它可能处于任何状态 - 任何东西都可以修改它。

我可以继续相当长一段时间,但要考虑的更大的概念是,事物的范围越紧,理由就越容易。我们擅长思考小事情,但如果没有模块化,就很难推断百万线路系统的状态。顺便说一句,这适用于所有类型的东西 - 而不仅仅是静态变量。

答案 1 :(得分:257)

它不是面向对象的: 有些人认为静态可能被认为是“邪恶”的一个原因是它们与object-oriented paradigm相反。特别是,它违反了数据被封装在对象中的原则(可以扩展,信息隐藏等)。在描述使用它们的方式中,静态主要是将它们用作全局变量,以避免处理范围等问题。然而,全局变量是程序或命令式编程范例的定义特征之一,而不是“好的”面向对象代码的特征。这并不是说程序范式很糟糕,但我得到的印象是你的主管希望你写出“好的面向对象的代码”而你真的想写出“好的程序代码”。

当你开始使用并不总是显而易见的静态时,Java中有许多新的。例如,如果您在同一个VM中运行了两个程序副本,它们是否会破坏静态变量的值并混淆彼此的状态?或者当你扩展类时会发生什么,你可以覆盖静态成员吗​​?你的虚拟机是否内存不足,因为你有大量的静态数据,并且无法为其他所需的实例对象回收内存?

对象生命周期: 此外,静态的生命周期与程序的整个运行时匹配。这意味着,即使您完成了使用您的类,所有这些静态变量的内存也无法进行垃圾回收。例如,如果您将变量设置为非静态,并且在main()函数中,您创建了类的单个实例,然后要求您的类执行10,000次特定函数,一旦完成10,000次调用,并删除对单个实例的引用,可以对所有静态变量进行垃圾回收和重用。

防止某些重复使用: 此外,静态方法不能用于实现接口,因此静态方法可以防止某些面向对象的特性可用。

其他选项: 如果效率是您的主要关注点,那么可能有其他更好的方法来解决速度问题,而不仅仅考虑调用的优势通常比创建更快。考虑是否需要瞬态或挥发性修饰符。为了保留内联的能力,可以将方法标记为final而不是static。方法参数和其他变量可以标记为final,以允许某些编译器优化基于对可以更改这些变量的内容的假设。实例对象可以多次重用,而不是每次都创建一个新实例。可能存在应该为应用程序打开的compliler优化开关。也许,应该设置设计,以便10,000次运行可以是多线程的,并利用多处理器内核。如果不考虑可移植性,那么本机方法可能会比静态方法更快。

如果出于某种原因你不想要一个对象的多个副本,singleton design pattern比静态对象有优势,比如线程安全(假设你的单例编码好),允许延迟初始化,保证在使用它时,对象已被正确初始化,子类化,测试和重构代码的优势,更不用说,如果在某些时候你改变主意只想要一个对象的一个​​实例,那么删除它会更容易用于防止重复实例的代码,而不是重构所有静态变量代码以使用实例变量。我以前必须这样做,它不是很有趣,你最终还是要编辑更多的类,这会增加你引入新bug的风险......更好的是第一次“正确”设置,即使看起来它有它的缺点。对我来说,如果你需要重新工作,你需要多次复制某些东西,这可能是尽可能不经常使用静力学的最有说服力的理由之一。因此我也不同意你的说法,即静态减少了相互依赖性,我认为如果你有很多可以直接访问的静态,你会得到更多耦合的代码,而不是一个“知道怎么做”的对象一些“本身。

答案 2 :(得分:91)

邪恶是一个主观的术语。

您无法在创建和销毁方面控制静态。他们生活在程序装载和卸载的要求下。

由于静态存在于一个空间中,所有希望使用它们的线程必须通过您必须管理的访问控制。这意味着程序更加耦合,这种变化更难以设想和管理(如J Skeet所说)。这导致隔离变更影响的问题,从而影响测试的管理方式。

这是我与他们的两个主要问题。

答案 3 :(得分:55)

没有。全球各州本身并不邪恶。但我们必须查看您的代码,看看您是否正确使用了它。新手很可能滥用全球各州;就像他会滥用所有语言特征一样。

全球各州绝对必要。我们无法回避全球各州。我们无法避免对全球国家的推理。 - 如果我们想了解我们的应用程序语义。

那些试图摆脱全球国家的人,不可避免地最终会得到一个更加复杂的系统 - 全球各州仍然存在,在许多层面的间接下巧妙地/孤独地伪装;在揭开所有的背景之后,我们仍然需要对全球国家进行推理。

就像春天的人们在xml中大肆宣布全球状态一样,并以某种方式思考它的优越性。

@Jon Skeet if I create a new instance of an object现在您有两件事需要解释 - 对象中的状态以及托管对象的环境状态。

答案 4 :(得分:30)

静态变量存在两个主要问题:

  • 线程安全 - 根据定义,静态资源不是线程安全的
  • Code Implicity - 您不知道何时实例化静态变量以及是否将在另一个静态变量之前实例化

答案 5 :(得分:27)

如果您使用的是'static'关键字而没有'final'关键字,那么这应该是一个仔细考虑您的设计的信号。即使存在'final'也不是免费传递,因为可变的静态最终对象可能同样危险。

我估计大约85%的时间我看到'静态'没有'最终',这是错误的。通常,我会找到一些奇怪的解决方法来掩盖或隐藏这些问题。

请不要创建静态变量。尤其是收藏品。通常,Collections应该在初始化其包含对象时进行初始化,并且应该进行设计,以便在忘记包含对象时重置或忘记它们。

使用静力学会产生非常微妙的错误,这将导致持续工程师痛苦的日子。我知道,因为我已经创造并捕获了这些错误。

如果您想了解更多详情,请继续阅读......

为什么不使用静态?

静态问题有很多问题,包括编写和执行测试,以及微不足道的错误。

依赖于静态对象的代码无法轻松进行单元测试,并且静态无法轻易模拟(通常)。

如果使用静态,则无法将类的实现交换出来以测试更高级别的组件。例如,假设一个静态CustomerDAO返回它从数据库加载的Customer对象。现在我有一个CustomerFilter类,需要访问一些Customer对象。如果CustomerDAO是静态的,我不能在没有首先初始化我的数据库并填充有用信息的情况下为CustomerFilter编写测试。

数据库填充和初始化需要很长时间。根据我的经验,您的数据库初始化框​​架将随着时间的推移而发生变化,这意味着数据会变形,测试可能会中断IE,假设客户1曾经是VIP,但数据库初始化框​​架已更改,现在客户1不再是VIP,但您的测试是硬编码加载客户1 ......

更好的方法是实例化CustomerDAO,并在构建CustomerFilter时将其传递给CustomerFilter。 (更好的方法是使用Spring或另一个Inversion of Control框架。

完成此操作后,您可以在CustomerFilterTest中快速模拟或删除备用DAO,从而可以更好地控制测试,

如果没有静态DAO,测试将更快(无数据库初始化)并且更可靠(因为当db初始化代码更改时它不会失败)。例如,在这种情况下,就测试而言,确保客户1始终是VIP。

执行测试

在一起运行单元测试套件时(例如,使用Continuous Integration服务器),静态会导致真正的问题。想象一下网络套接字对象的静态映射,它从一个测试到另一个测试保持打开状态。第一个测试可能会在端口8080上打开一个Socket,但是当测试被拆除时你忘了清除Map。现在,当第二次测试启动时,它可能会在尝试为端口8080创建新的Socket时崩溃,因为端口仍然处于占用状态。想象一下,静态Collection中的Socket引用不会被删除,并且(WeakHashMap除外)永远不会被垃圾收集,导致内存泄漏。

这是一个过于笼统的例子,但在大型系统中,这个问题一直都在发生。人们不会想到在同一个JVM中重复启动和停止软件的单元测试,但它是对您的软件设计的一个很好的测试,如果您对高可用性抱有期望,那么您需要注意这一点。 / p>

这些问题经常出现在框架对象中,例如,您的数据库访问,缓存,消息传递和日志记录层。如果你正在使用Java EE或一些最好的框架,他们可能会为你管理很多这样的东西,但是如果像我一样你正在处理遗留系统,你可能有很多自定义框架来访问这些层。

如果适用于这些框架组件的系统配置在单元测试之间发生更改,并且单元测试框架没有拆除并重建组件,则这些更改不会生效,并且当测试依赖于这些更改时,他们会失败。

即使是非框架组件也会遇到此问题。想象一下名为OpenOrders的静态地图。您编写了一个创建一些打开订单的测试,并检查以确保它们都处于正确的状态,然后测试结束。另一个开发人员编写第二个测试,将所需的订单放入OpenOrders映射,然后断言订单的数量是准确的。单独运行,这些测试都会通过,但是当它们在套件中一起运行时,它们将会失败。

更糟糕的是,失败可能基于测试的运行顺序。

在这种情况下,通过避免静态,您可以避免跨测试实例持久保存数据的风险,从而确保更好的测试可靠性。

微妙的错误

如果您在高可用性环境中工作,或者在线程可能启动和停止的任何地方工作,那么当您的代码也在生产中运行时,上面提到的与单元测试套件相同的问题也适用。

在处理线程时,不是使用静态对象来存储数据,最好使用在线程启动阶段初始化的对象。这样,每次启动线程时,都会创建一个对象的新实例(具有可能的新配置),并且您可以避免来自该线程的一个实例的数据流入下一个实例。

当线程死亡时,静态对象不会被重置或垃圾回收。想象一下,你有一个名为“EmailCustomers”的线程,当它启动时,它会填充一个带有电子邮件地址列表的静态String集合,然后开始通过电子邮件发送每个地址。让我们说线程被中断或以某种方式取消,因此您的高可用性框架重新启动线程。然后当线程启动时,它会重新加载客户列表。但由于该集合是静态的,因此它可能会保留上一个集合中的电子邮件地址列表。现在有些客户可能会收到重复的电子邮件。

旁白:静态决赛

“静态final”的使用实际上是C #define的Java等价物,尽管存在技术实现差异。在编译之前,预处理器将C / C ++ #define换出代码。 Java“静态final”将结束驻留在堆栈上的内存。这样,它更像C ++中的“static const”变量而不是#define。

<强>摘要

我希望这有助于解释为什么静力学有问题的一些基本原因。如果您使用的是Java EE或Spring等现代Java框架,您可能不会遇到很多这样的情况,但如果您使用大量遗留代码,它们可能会变得更加频繁。

答案 6 :(得分:15)

因为没有人提到它:并发。如果有多个线程读写静态变量,静态变量会让你大吃一惊。这在Web应用程序(例如,ASP.NET)中很常见,它可能会导致一些令人抓狂的错误。例如,如果您有一个由页面更新的静态变量,并且该页面由“几乎同时”的两个人请求,则一个用户可能会得到另一个用户期望的结果,或者更糟。

  

静态减少了代码其他部分的相互依赖性。他们可以充当完美的国家持有者

我希望你准备好使用锁并处理争用。

*实际上,Preet Sangha提及了它。

答案 7 :(得分:13)

  

如果我必须对一个类中的函数进行10,000次调用,我会这样做   很高兴使方法静态并使用简单   class.methodCall()就可以了,而不是用10,000来混乱内存   班级的实例,对吗?

您必须平衡将数据封装到具有状态的对象的需求,而不是简单地计算某些数据上的函数结果的需要。

  

此外,静态减少了对代码其他部分的依赖性。

封装也是如此。在大型应用程序中,静态函数往往会生成意大利面条代码,并且不容易进行重构或测试。

其他答案也提供了反对过度使用静力学的充分理由。

答案 8 :(得分:12)

静态变量通常被认为是错误的,因为它们代表全局状态,因此更难以推理。特别是,它们打破了面向对象编程的假设。在面向对象的编程中,每个对象都有自己的状态,由实例(非静态)变量表示。静态变量表示跨实例的状态,单元测试可能要困难得多。这主要是因为将静态变量的更改隔离到单个测试更加困难。

话虽如此,重要的是区分常规静态变量(通常认为是坏的)和最终的静态变量(AKA常量;不是那么糟糕)。

答案 9 :(得分:10)

总结几个基本优势&amp;在Java中使用静态方法的缺点:

<强>优点:

  1. 全球可访问,即不与任何特定对象实例绑定。
  2. 每个JVM一个实例。
  3. 可以使用类名访问(无需对象)。
  4. 包含适用于所有实例的单个值。
  5. 加载JVM启动并在JVM关闭时死亡。
  6. 他们没有修改对象的状态。
  7. <强>缺点:

    1. 静态成员始终是内存的一部分,无论它们是否在使用中。
    2. 您无法控制静态变量的创建和销毁。有用的是,它们是在程序加载时创建的,并在程序卸载时(或当JVM关闭时)被销毁。
    3. 您可以使用同步使静态线程安全,但需要额外的努力。
    4. 如果一个线程更改了可能会破坏其他线程功能的静态变量的值。
    5. 使用前必须知道“静态”。
    6. 您无法覆盖静态方法。
    7. 序列化并不适合他们。
    8. 他们没有参与运行时多态性。
    9. 如果使用大量静态变量/方法,则存在内存问题(在某种程度上,但我猜不多)。因为在程序结束之前它们不会被收集。
    10. 静态方法也很难测试。

答案 10 :(得分:9)

在我看来,这几乎与性能无关,而是关于设计。我不认为静态方法的使用错误,因为使用了静态变量(但我猜你实际上是在谈论方法调用)。

这只是关于如何隔离逻辑并给它一个好地方。有时这证明使用静态方法是合理的,java.lang.Math就是一个很好的例子。我认为,当您为大多数课程命名XxxUtilXxxhelper时,您最好重新考虑您的设计。

答案 11 :(得分:6)

我刚刚总结了答案中提出的一些观点。如果您发现任何错误,请随时纠正。

缩放:每个JVM只有一个静态变量实例。假设我们正在开发一个图书管理系统,我们决定将图书名称作为静态变量,因为每本书只有一个。但是,如果系统增长并且我们使用多个JVM,那么我们无法找出我们正在处理哪本书?

线程安全:在多线程环境中使用时,需要控制实例变量和静态变量。但是在实例变量的情况下,它不需要保护,除非它在线程之间显式共享,但是在静态变量的情况下,它总是由进程中的所有线程共享。

测试虽然可测试的设计不等于好的设计但我们很少会观察到一个不可测试的好设计。由于静态变量代表全局状态,因此很难对它们进行测试。

关于状态的推理:如果我创建一个类的新实例,那么我们可以推断出这个实例的状态,但是如果它有静态变量那么它可以处于任何状态。为什么?因为静态变量可能已被某个不同的实例修改,因为静态变量是跨实例共享的。

序列化:序列化对它们也不起作用。

创建和销毁:无法控制静态变量的创建和销毁。通常它们是在程序加载和卸载时创建和销毁的。这意味着它们对内存管理不利,并且还会在启动时累计初始化时间。

但是,如果我们真的需要它们呢?

但有时候我们可能真的需要它们。如果我们真的觉得需要在应用程序中共享许多静态变量,那么一个选项就是使用具有所有这些变量的Singleton Design模式。或者我们可以创建一些具有这些静态变量的对象,并且可以传递。

此外,如果静态变量标记为final,则它变为常量,并且一旦分配给它,则无法更改。这意味着它将使我们免于因其可变性而面临的所有问题。

答案 12 :(得分:6)

可能会建议您在使用静态变量的大多数情况下,确实希望使用singleton pattern

全局状态的问题在于,有时在更简单的上下文中有意义的全局变量,在实际环境中需要更加灵活,这就是单例模式变得有用的地方。

答案 13 :(得分:6)

在我看来,你问的是静态变量,但你也在示例中指出了静态方法。

静态变量并不是邪恶的 - 它们作为全局变量被采用,例如在大多数情况下常量与最终修饰符结合使用,但正如它所说的那样,不要过度使用它们。

静态方法又称实用方法。使用它们通常不是一种坏习惯,但主要担心的是它们可能obstruct进行测试。

作为一个使用大量静态并且正确执行的优秀java项目的示例,请查看Play! framework。在SO中也有discussion

静态变量/方法与静态导入相结合,也广泛用于便于java中声明性编程的库中,如:make it easyHamcrest。没有很多静态变量和方法就不可能。

所以静态变量(和方法)很好,但要明智地使用它们!

答案 14 :(得分:6)

静态变量最重要的是会产生数据安全性问题(任何时候都会发生变化,任何人都可以更改,无需对象直接访问等)。

有关详细信息,请阅读this 感谢。

答案 15 :(得分:5)

另一个原因:脆弱。

如果您有课程,大多数人都希望能够创建并随意使用它。

你可以记录不是这种情况,或者防止它(单件/工厂模式) - 但这是额外的工作,因此需要额外的费用。 即便如此,在一家大公司里,有些人可能会尝试使用你的课程而不必完全关注所有好评或工厂。

如果你经常使用静态变量,那就会破坏。虫子很贵。

在可能无能的开发人员的性能改进和稳健性改进之间,在很多情况下,稳健性是不错的选择。

答案 16 :(得分:4)

“静电是邪恶的”问题更多的是关于全球国家的问题。变量是静态的适当时间,如果它不具有多个状态;整个框架应该可以访问的IE工具总是为相同的方法调用返回相同的结果,这绝不是静态的“邪恶”。至于你的评论:

  

我发现静态变量使用起来更方便。而且我认为它们也很有效率

静态是无变化的变量/类的理想且有效的选择

全局状态的问题是它可以创建的固有的不一致性。有关单元测试的文档经常解决这个问题,因为任何时候有一个全局状态可以被多个不相关的对象访问,你的单元测试将是不完整的,而不是“单元”粒度。如本文中关于global state and singletons所述,如果对象A和B不相关(如同一个未明确指出另一个),那么A不应该影响B的状态。

禁用全局状态有一些例外,例如时钟。时间是全局的,并且 - 在某种意义上 - 它改变了对象的状态而没有编码关系。

答案 17 :(得分:4)

我发现静态变量使用起来更方便。而且我认为它们也是有效的(如果我错了,请纠正我)因为如果我必须对类中的函数进行10,000次调用,我很乐意将该方法设置为static并使用简单的class.methodCall()在它上面而不是用10,000个类的实例来混乱内存,对吗?

我看到你的想法,但是一个简单的Singleton模式也可以做同样的事情,而不必实例化10000个对象。

可以使用静态方法,但仅适用于与对象域相关且不需要或使用对象内部属性的函数。

例如:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

答案 18 :(得分:4)

我的$ .02是这些答案中有几个令人困惑的问题,而不是说“静态不好”我认为更好地讨论范围和实例。

我要说的是静态是一个“类”变量 - 它表示在该类的所有实例之间共享的值。通常它也应该以这种方式作用域(对类及其实例保护或私有)。

如果您打算在其周围放置类级行为并将其暴露给其他代码,那么单例可能是支持未来更改的更好解决方案(如@Jessica建议的那样)。这是因为您可以使用在类级别无法使用的方式在实例/单例级别使用接口 - 特别是继承。

关于为什么我认为其他答案中的某些方面不是问题核心的一些想法......

静力不是“全球性的”。在Java中,作用域与静态/实例分开控制。

对于静态而言,并发性并不比实例方法危险。它仍然是需要保护的状态。当然,你可能有1000个实例,每个实例变量只有一个静态变量,但是如果访问的代码不是以线程安全的方式编写的,那么你仍然会被搞砸 - 它可能需要更长的时间才能实现它

管理生命周期是一个有趣的论点,但我认为它不那么重要。我不明白为什么管理一对类方法(如init()/ clear()比创建和销毁单例实例更难)。事实上,有些人可能会说,由于GC,单身人士会更复杂一些。

PS,就Smalltalk而言,它的许多方言都有类变量,但Smalltalk类实际上是Metaclass的实例,所以它们实际上是Metaclass实例上的变量。不过,我会采用相同的经验法则。如果它们被用于跨实例的共享状态那么确定。如果他们支持公共功能,你应该看一个Singleton。叹了口气,我确实错过了Smalltalk ....

答案 19 :(得分:4)

静态变量本身并没有错。只是Java语法被打破了。每个Java类实际上定义了两个结构 - 一个封装静态变量的单例对象和一个实例。在同一个源块中定义两者是纯粹的邪恶,并导致难以阅读的代码。斯卡拉做得对。

答案 20 :(得分:3)

如果你有大量的线程需要共享/缓存数据以及所有可访问的内存(所以你不要分成一个JVM中的上下文)静态是最佳选择

- &gt;当然你可以只强制一个实例,但为什么呢? 我发现这个帖子中的一些评论是邪恶的,而不是静态的;)

答案 21 :(得分:3)

您的帖子中有两个主要问题。

首先,关于静态变量。 静态变量完全不需要,可以很容易地避免使用它。在OOP语言中,特别是在Java中,函数参数通过引用来表示,也就是说,如果将对象传递给函数,则传递指向对象的指针,因此您不需要定义静态变量,因为您可以将指向该对象的指针传递给需要此信息的任何范围。即使这意味着你将用指针填充你的内存,但这并不代表性能不佳,因为实际的内存分页系统已经过优化以处理这个问题,并且它们将在内存中保存你传递给新的指针引用的页面范围;静态变量的使用可能导致系统在需要访问它们时加载存储它们的存储页面(如果页面长时间没有被接收,就会发生这种情况)。一个好的做法是将所有静态stuf放在一些小的“配置条款”中,这将确保系统将所有内容放在同一个内存页面中。

其次,关于静态方法。 静态方法并不是那么糟糕,但它们可以快速降低性能。例如,考虑一个方法,它比较一个类的两个对象,并返回一个值,指示哪个对象更大(tipical比较方法)这个方法可以是静态的,但是当调用它时,非静态形式将更有效因为它必须只解决两个引用(每个对象一个)面向三个引用,这三个引用必须解决同一方法的静态版本(一个用于类加两个,每个对象一个)。但正如我所说,这并不是那么糟糕,如果我们看一下Math类,我们可以找到许多定义为静态方法的数学函数。这比将所有这些方法都放在定义数字的类中更有效,因为大多数方法都被使用得很多,并且在数字类中包含所有这些方法都会导致类非常复杂并且不必要地消耗大量资源。 / p>

结论:在处理静态或非静态方法时,避免使用静态变量并找到正确的性能平衡。

PS:对不起我的英文。

答案 22 :(得分:3)

a)关于节目的原因。

如果你有一个中小型程序,访问静态变量Global.foo,对它的调用通常来自任何地方 - 没有路径,因此没有时间轴,变量如何来到这个地方,在哪里使用。现在我如何知道谁将其设定为实际值?如果我现在修改它,我怎么知道,会发生什么?我对整个来源感兴趣,收集所有访问,知道,发生了什么。

如果您知道如何使用它,因为您只是编写代码,问题是不可见的,但如果您尝试理解外部代码,您就会明白。

b)你真的只需要一个吗?

静态变量通常会阻止在具有不同值的同一JVM中运行同一类型的多个程序。您经常不会预见到一些程序实例有用的用法,但是如果它发展了,或者如果它对其他人有用,他们可能会遇到这样的情况,他们希望启动多个程序实例。

只有更多或更少的无用代码在很长一段时间内不会被密集使用,这可能与静态变量相得益彰。

答案 23 :(得分:2)

上面的所有答案都说明了为什么静力学是坏的。他们邪恶的原因是因为它给人以错误的印象,即你正在编写面向对象的代码,而事实上你并非如此。 这简直就是邪恶。

答案 24 :(得分:2)

静态变量不好也不邪恶。它们表示描述整个类而不是特定实例的属性。如果您需要为某个类的所有实例设置一个计数器,则静态变量将是保存该值的正确位置。

当您尝试使用静态变量来保存与实例相关的值时,会出现问题。

答案 25 :(得分:1)

这里有很多好的答案,加上它,

内存:  只要类加载器存在[通常直到VM死亡],静态变量就会生效,但这只是在大量对象/引用存储为静态的情况下。

模块化: 考虑像IOC,dependencyInjection,代理等概念。所有这些都完全反对紧密耦合/静态实现。

其他Con:线程安全性,可测试性

答案 26 :(得分:0)

如果您有一个包含许多用户的应用程序并且您已定义静态表单,那么每个用户也会修改其他用户的所有其他形式。

答案 27 :(得分:0)

我认为使用static关键字过度使用全局变量也会导致应用程序中某个实例的内存泄漏

答案 28 :(得分:0)

从我的角度来看,static变量应该仅是只读数据或按惯例创建的变量

>

例如,我们有一个项目的ui,并且有一个国家/地区,语言,用户角色等的列表。并且我们有用于组织此数据的类。我们绝对确定,没有此列表,该应用程序将无法运行。因此,我们在应用程序初始化中首先要做的是检查此列表是否有更新,并从api获取此列表(如果需要)。因此,我们同意此数据“始终”存在于应用程序中。它实际上是只读数据,因此我们不需要照顾它的状态-考虑到这种情况,我们真的不想拥有这些数据的很多实例-这种情况看起来很适合作为静态

答案 29 :(得分:0)

我玩过很多静态音乐,可以给你一个略有不同的答案吗?或者也许是一个略有不同的查看方式?

当我在一个类(成员和方法均使用)中使用了静态变量时,我最终开始注意到我的类实际上是两个共同承担责任的类-“静态”部分的作用与单例类似,是非静态部分(正常类)。据我所知,您始终可以通过只为一个类选择所有静态变量而为另一个选择非静态变量来完全分离这两个类。

当我在一个类中拥有一个包含类实例的静态集合以及一些用于管理该集合的静态方法时,这种情况经常发生。一旦您考虑了一下,很明显您的班级并没有在做“仅一件事”,而是在做一个集合,并且所做的事情完全不同。

现在,让我们稍微重构一下问题:如果将您的班级划分为一类,其中所有内容都是静态的,而另一类只是“ Normal Class”,而忽略了“ Normal Class”,那么您的问题将变成纯静态类vs Singleton,其长度为here(可能还有其他十二个问题)。