关于Java:静态变量为什么被认为是邪恶的?

Why are static variables considered evil?

我是一个Java程序员,对企业界很陌生。最近我开发了一个使用Groovy和Java的应用程序。在我编写的整个代码中,使用了相当多的静态代码。高级技术组要求我减少使用的静力学量。我在google上搜索过类似的内容,我发现很多程序员都反对使用静态变量。

我发现静态变量更容易使用。我假设它们也是有效的(如果我错了请纠正我),因为如果我必须对一个类中的一个函数进行10000次调用,我会很高兴使该方法成为静态的,并在它上面使用一个简单的Class.methodCall(),而不是将内存与该类的10000个实例混在一起,对吗?

此外,静态减少了对代码其他部分的相互依赖。他们可以充当完美的国家持有者。除此之外,我发现statics在一些语言(如smalltalk和scala)中得到了广泛的实现。那么为什么这种静力学的压制在程序员(尤其是Java世界)中普遍存在呢?

附言:如果我对静态的假设是错误的,请纠正我。


静态变量表示全局状态。这很难解释,也很难测试:如果我创建一个对象的新实例,我可以在测试中解释它的新状态。如果我使用使用静态变量的代码,它可能处于任何状态——任何事情都可能修改它。

我可以继续讲很长一段时间,但更大的概念是,范围越窄,推理就越容易。我们擅长思考一些小问题,但是如果没有模块化,就很难解释一百万行系统的状态。顺便说一句,这适用于所有类型的事情——不仅仅是静态变量。


它不是很面向对象:有些人可能认为静态是"邪恶"的一个原因是它们与面向对象的范式相反。尤其是,它违反了将数据封装到对象中(可以扩展、信息隐藏等)的原则。静态,在您描述使用它们的方式中,本质上是将它们作为一个全局变量来使用,以避免处理诸如范围之类的问题。然而,全局变量是过程或命令式编程范式的定义特征之一,而不是面向对象的"好"代码的特征。这并不是说过程范式不好,但我觉得您的主管希望您编写"好的面向对象的代码",而实际上您希望编写"好的过程代码"。好的。

在爪哇,当你开始使用静力学并不总是显而易见的。例如,如果您的程序有两个副本在同一个虚拟机中运行,那么它们是否会对静态变量的值进行预处理并相互干扰状态?或者在扩展类时会发生什么,您可以重写静态成员吗?您的虚拟机是否因为静态数据太多而内存不足,并且无法为其他需要的实例对象回收内存?好的。

对象生存期:此外,静态的生存期与程序的整个运行时相匹配。这意味着,即使使用完类,所有这些静态变量的内存也不能被垃圾收集。例如,如果您将变量设为非静态的,并且在main()函数中,您创建了类的一个实例,然后要求类执行一个特定的函数10000次,一旦这些10000次调用完成,并且删除了对单个实例的引用,那么所有静态变量都可以被垃圾收集。钕再利用。好的。

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

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

如果出于某种原因您不需要一个对象的多个副本,那么与静态对象相比,单例设计模式具有优势,例如线程安全(假定您的单例代码编写得很好)、允许延迟初始化、确保对象在使用时已正确初始化、子类化、测试和重构方面的优势。您的代码,更不用说,如果在某个时刻您改变了只需要一个对象实例的想法,那么删除代码以防止重复实例要比重构所有静态变量代码以使用实例变量容易得多。我以前必须这样做,这并不有趣,你最终不得不编辑更多的类,这增加了引入新bug的风险…所以最好在第一次设置"正确"的东西,即使它看起来有缺点。对我来说,如果你在路上决定需要多个副本,那么所需的重新工作可能是尽可能少地使用静态的最令人信服的原因之一。因此,我也不同意您关于静态减少相互依赖性的说法,我认为如果您有许多可以直接访问的静态,而不是一个"知道如何做事情"的对象,那么您最终将得到更耦合的代码。好的。好啊。


邪恶是一个主观术语。

在创建和破坏方面,您不能控制静态。他们生活在程序加载和卸载的要求下。

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

这是我和他们之间的两个主要问题。


不,全球国家本身并不邪恶。但是我们必须看到您的代码,看看您是否正确地使用了它。一个新手很有可能滥用全球各州,就像他滥用每一种语言特征一样。

全球国家是绝对必要的。我们不能回避全球国家。我们不能回避对全球国家的推理。-如果我们想了解我们的应用程序语义。

为了摆脱全球国家,人们不可避免地会得到一个更加复杂的体系——全球国家仍然存在,巧妙地/白痴地伪装在许多间接的层次之下;我们仍然必须在拆解所有间接之后,对全球国家进行思考。

像Spring的人一样,他们慷慨地用XML声明全局状态,并且认为它在某种程度上更优越。

@jon skeet if I create a new instance of an object现在有两件事要解释——对象内的状态和对象所在环境的状态。


静态变量有两个主要问题:

  • 线程安全-静态资源的定义不是线程安全的
  • 代码含义-您不知道静态变量何时被实例化,以及它是否将在另一个静态变量之前被实例化。


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

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

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

使用静力学可以产生非常细微的错误,这将导致持续工程师数天的痛苦。我知道,因为我已经创造并猎杀了这些虫子。好的。

如果您想了解更多详情,请继续阅读…好的。

为什么不使用静力学?好的。

静态有许多问题,包括编写和执行测试,以及不明显的细微错误。好的。

依赖静态对象的代码不容易进行单元测试,静态也不容易被模拟(通常)。好的。

如果使用statics,则不可能为了测试更高级别的组件而交换类的实现。例如,假设一个静态的customerdo返回它从数据库加载的customer对象。现在我有了一个类customerfilter,它需要访问一些customer对象。如果customerdao是静态的,那么如果不首先初始化数据库并填充有用的信息,我就无法为customerfilter编写测试。好的。

数据库的填充和初始化需要很长时间。根据我的经验,您的数据库初始化框架将随着时间而改变,这意味着数据将变形,测试可能会中断。例如,假设客户1以前是VIP,但是数据库初始化框架改变了,现在客户1不再是VIP,但是您的测试被硬编码为加载客户1…好的。

一种更好的方法是实例化一个customerdao,并在构造它时将其传递给customerfilter。(更好的方法是使用Spring或另一个反向控制框架。好的。

一旦你这样做了,你就可以在你的customerfiltertest中快速地模拟或删除一个备用DAO,让你对测试有更多的控制权。好的。

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

测试的执行好的。

静态在一起运行单元测试套件(例如,使用持续集成服务器)时会导致真正的问题。设想一个静态的网络套接字对象映射,它从一个测试一直打开到另一个测试。第一个测试可能会打开端口8080上的套接字,但是当测试被破坏时,您忘记清除映射。现在,当第二个测试启动时,当它试图为端口8080创建一个新的套接字时,很可能会崩溃,因为该端口仍然被占用。另外,假设静态集合中的套接字引用没有被删除,并且(weakhashmap除外)永远不符合被垃圾收集的条件,从而导致内存泄漏。好的。

这是一个过于普遍化的例子,但是在大系统中,这个问题一直都在发生。人们不认为单元测试在同一个JVM中重复地启动和停止他们的软件,但是这是对软件设计的一个很好的测试,如果您有高可用性的愿望,那么这是您需要注意的。好的。

这些问题通常出现在框架对象中,例如,数据库访问、缓存、消息传递和日志记录层。如果您使用的是JavaEE或一些最优秀的框架,它们可能为您管理了很多这些,但是如果像我一样,您正在处理遗留系统,那么您可能有很多自定义框架来访问这些层。好的。

如果应用于这些框架组件的系统配置在单元测试之间发生更改,并且单元测试框架没有分解和重建组件,那么这些更改将不会生效,并且当测试依赖于这些更改时,它们将失败。好的。

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

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

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

微妙的错误好的。

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

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

当线程死亡时,静态对象不会被重置或垃圾收集。假设您有一个名为"emailcustomers"的线程,当它启动时,会用电子邮件地址列表填充静态字符串集合,然后开始向每个地址发送电子邮件。假设线程以某种方式被中断或取消,那么高可用性框架将重新启动线程。然后当线程启动时,它重新加载客户列表。但由于该集合是静态的,因此它可能保留以前集合中的电子邮件地址列表。现在有些客户可能会收到重复的电子邮件。好的。

旁白:静态决赛好的。

使用"静态终结"实际上是Java等效的C语言定义,虽然存在技术实现上的差异。在编译前,通过预处理器将C/C++的定义从代码中交换出来。Java"静态最终"将结束堆栈上驻留的内存。这样,它比C++中的"静态const"变量更类似于一个y*定义。好的。

总结好的。

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


因为没有人提到它:并发性。如果有多个线程读写静态变量,静态变量可能会让您感到惊讶。这在Web应用程序(如ASP.NET)中很常见,它可能会导致一些令人恼火的错误。例如,如果一个页面更新了一个静态变量,并且两个人"几乎同时"请求该页面,那么一个用户可能会得到另一个用户预期的结果,或者更糟。

statics reduce the inter-dependencies on the other parts of the code. They can act as perfect state holders

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

*实际上,普瑞特·桑加提到过。


if I had to make 10,000 calls to a function within a class, I would be
glad to make the method static and use a straightforward
class.methodCall() on it instead of cluttering the memory with 10,000
instances of the class, Right?

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

Moreover statics reduce the inter-dependencies on the other parts of the code.

封装也是如此。在大型应用程序中,静态通常会生成意大利面代码,并且不容易允许重构或测试。

其他答案也为防止过度使用静力学提供了很好的理由。


静态变量通常被认为是坏的,因为它们代表全局状态,因此更难解释。特别是,它们打破了面向对象编程的假设。在面向对象编程中,每个对象都有自己的状态,由实例(非静态)变量表示。静态变量表示跨实例的状态,这对于单元测试来说可能更困难。这主要是因为很难将静态变量的变化隔离到单个测试中。

也就是说,区分常规静态变量(通常被认为是坏的)和最终静态变量(也就是常量,不是很坏)是很重要的。


总结Java中使用静态方法的几个基本优点和缺点:

优势:

  • 全局可访问,即不与任何特定对象实例绑定。
  • 每个JVM一个实例。
  • 可以使用类名访问(不需要对象)。
  • 包含适用于所有实例的单个值。
  • 在JVM启动时加载,并在JVM关闭时死亡。
  • 它们不会修改对象的状态。
  • 缺点:

  • 静态成员总是内存的一部分,不管它们是否在使用中。
  • 不能控制静态变量的创建和销毁。有用的是,它们是在程序加载时创建的,在程序卸载时(或在JVM关闭时)销毁。
  • 可以使用synchronize使statics线程安全,但需要做一些额外的工作。
  • 如果一个线程更改了可能破坏其他线程功能的静态变量的值。
  • 在使用之前,您必须了解"静态"。
  • 不能重写静态方法。
  • 序列化对它们不起作用。
  • 它们不参与运行时多态性。
  • 如果使用大量的静态变量/方法,则会出现内存问题(在某种程度上,但我想不会有太多)。因为在程序结束之前它们不会被垃圾收集。
  • 静态方法也很难测试。

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

    它只是关于如何隔离逻辑并给它一个好的位置。有时,这证明使用静态方法是正确的,java.lang.Math就是一个很好的例子。我认为,当你把你的大部分课程命名为XxxUtilXxxhelper时,你最好重新考虑一下你的设计。


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

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

    静态方法也称为实用方法。使用它们通常不是一个坏的实践,但主要的问题是它们可能会妨碍测试。

    作为一个伟大的Java项目的例子,使用很多静态和正确的方式,请看游戏!框架。在SO中也有关于它的讨论。

    与静态导入相结合的静态变量/方法也广泛地应用于促进Java中声明性编程的库中:使其变得简单或HAMCSTREST。没有很多静态变量和方法是不可能的。

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


    静态变量最重要的是会对数据的安全性造成问题(任何时候更改,任何人都可以更改,直接访问而不使用对象等)。

    欲了解更多信息,请阅读此谢谢。


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

    缩放:每个JVM只有一个静态变量的实例。假设我们正在开发一个图书馆管理系统,我们决定将图书的名称作为一个静态变量,因为每本书只有一个变量。但是,如果系统增长,我们使用多个JVM,那么我们就没有办法弄清楚我们要处理的是哪本书了?

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

    测试:尽管可测试的设计并不等于好的设计,但我们很少观察到不可测试的好设计。由于静态变量代表全局状态,因此很难对其进行测试。

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

    序列化:序列化也不能很好地处理它们。

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

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

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

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


    可能会建议在大多数情况下使用静态变量时,您确实希望使用单例模式。

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


    还有一个原因:脆弱。

    如果您有一个类,大多数人希望能够创建它并随意使用它。

    您可以记录不是这种情况,也可以防范(单例/工厂模式),但这是额外的工作,因此需要额外的成本。即便如此,在一家大公司里,也有可能有人会在某个时候尝试使用你的课程,而不完全关注所有的好评论或工厂。

    如果你经常使用静态变量,这会被打破。虫子很贵。

    在0.0001%的性能改进和潜在的无头绪开发人员对更改的稳健性之间,在很多情况下稳健性是一个不错的选择。


    我发现静态变量更容易使用。我假设它们也是有效的(如果我错了请纠正我),因为如果我必须对一个类中的一个函数进行10000次调用,我会很高兴使该方法成为静态的,并对它使用一个简单的类.methodCall(),而不是将内存与该类的10000个实例混淆,对吗?

    我明白你的想法,但是一个简单的单例模式也可以做到这一点,而不需要实例化10000个对象。

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

    前任:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class WaterContainer {
        private int size;
        private int brand;
        ...etc

        public static int convertToGallon(int liters)...

        public static int convertToLiters(int gallon)...

    }


    "静力学是邪恶的"的问题更多的是关于全球国家的问题。变量处于静态状态的适当时间是,如果它不具有多个状态;即整个框架应该可以访问的工具,并且总是为相同的方法调用返回相同的结果,这些工具决不会像statics那样"邪恶"。关于你的评论:

    I find static variables more convenient to use. And I presume that they are efficient too

    对于永远不会改变的变量/类,静态是理想而有效的选择。

    全局状态的问题是它可以创建的固有的不一致性。关于单元测试的文档经常解决这个问题,因为任何时候有一个全局状态可以被多个不相关的对象访问,您的单元测试将是不完整的,而不是"单元"粒度的。正如本文中关于全局状态和单子态所提到的,如果对象A和B是无关的(正如其中一个没有明确地提到另一个),那么A不应该影响B的状态。

    在良好的代码中,有一些关于禁止全局状态的例外,例如时钟。时间是全局的,在某种意义上,它改变了对象的状态,而没有编码的关系。


    我的$0.02是这些答案中有几个会混淆问题,而不是说"静态是坏的",我认为讨论范围界定和实例更好。

    我想说的是,静态变量是一个"类"变量——它表示在该类的所有实例中共享的值。通常,它的作用域也应该是这样的(对类及其实例是受保护的或私有的)。

    如果您计划将类级行为放在它周围并将其公开给其他代码,那么单例方法可能是将来支持更改的更好的解决方案(如@jessica建议的那样)。这是因为您可以在实例/单例级别使用接口,而不能在类级别使用接口,尤其是继承接口。

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

    静态不是"全局"的。在Java范围内,与静态/实例分开进行控制。

    并发性对静态的危害不亚于实例方法。它仍然是需要保护的状态。当然,您可能有1000个实例,每个实例变量只有一个静态变量,但是如果访问其中一个实例的代码不是以线程安全的方式编写的,那么您仍然会陷入困境——您可能需要更长的时间来实现它。

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

    ps,就smalltalk而言,它的许多方言都有类变量,但在smalltalk类中,实际上是元类的实例,因此它们实际上是元类实例上的变量。不过,我还是会用同样的经验法则。如果它们被用于跨实例的共享状态,那么OK。如果它们支持公共功能,那么您应该查看单个实例。叹息,我真的很怀念斯莫尔塔克……


    静态变量本身没有问题。只是Java语法被破坏了。每个Java类实际上定义了两个结构——一个封装静态变量的单体对象和一个实例。在同一个源代码块中定义这两个代码是完全错误的,并且会导致难以读取的代码。斯卡拉做得对。


    a)程序原因。

    如果您有一个中小型程序,在该程序中访问静态变量global.foo,那么对它的调用通常不从任何地方发出——没有路径,因此没有时间线,变量是如何到达使用位置的。现在,我怎么知道是谁将它设置为实际值的呢?我怎么知道,如果我现在修改它会发生什么?我对整个源代码都有grep,收集所有的访问,知道发生了什么。

    如果您知道如何使用它,因为您刚刚编写了代码,那么问题是不可见的,但是如果您试图理解外部代码,那么您将能够理解。

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

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

    只有或多或少无用的代码(这些代码在长时间内不会被许多人集中使用)才能很好地处理静态变量。


    你的文章有两个主要问题。

    首先,关于静态变量。静态变量是不必要的,很容易避免使用。一般来说,在OOP语言中,特别是在Java中,函数参数是通过引用来标记的,也就是说,如果将对象传递给FuncIONT,则传递一个指向对象的指针,因此不需要定义静态变量,因为可以将指针传递给需要此信息的任何范围。即使这意味着yo将用指针填充您的内存,这也不一定表示性能差,因为实际的内存分页系统经过优化可以处理这一点,并且它们将在内存中保留您传递到新作用域的指针所引用的页;使用静态变量可能会导致系统加载需要访问时存储它们的内存页(如果长时间未访问该页,则会发生这种情况)。一个好的实践是将所有静态stuf放在一些小的"配置类"中,这将确保系统将所有静态stuf放在同一个内存页中。

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

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

    PS:对不起,我的英语。


    一切(can:)都有它的目的,如果你有一堆需要共享/缓存数据的线程,以及所有可访问的内存(这样你就不会在一个JVM中拆分为上下文),那么静态是最好的选择->当然,你可以只强制一个实例,但是为什么呢?我发现这条线中的一些评论是邪恶的,而不是静态的;)


    上面所有的答案都说明了为什么静态是坏的。它们之所以邪恶,是因为它给人一种错误的印象,即您正在编写面向对象的代码,而实际上您不是。这简直就是邪恶。


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

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


    这里有很多好的答案,除此之外,

    记忆:静态变量的生存时间与类加载器的生存时间相同[通常在VM死之前],但这仅适用于存储为静态的批量对象/引用。

    Modularization:考虑IOC、依赖性拒绝、代理等概念。所有这些都完全反对紧密耦合/静态实现。

    其他配置:线程安全性、可测试性


    从我的观点来看,static变量只能是只读数据或由约定创建的变量。

    例如,我们有一个某个项目的UI,我们有一个国家、语言、用户角色等的列表,并且我们有类来组织这些数据。如果没有这个列表,我们肯定这个应用程序不会工作。所以我们在app init上做的第一件事就是检查这个列表的更新,并从api获取这个列表(如果需要的话)。所以我们同意这些数据"总是"存在于应用程序中。它实际上是只读数据,所以我们不需要考虑它的状态——考虑到这个案例,我们真的不想有很多这些数据的实例——这个案例看起来是静态的完美候选。


    如果您有一个拥有许多用户的应用程序,并且您定义了一个静态表单,那么每个用户也将修改其他用户的所有其他表单。


    我认为使用带有静态关键字的全局变量的过度使用也会导致applica中某些实例的内存泄漏。