我是一名Java程序员,刚进入企业界。最近我用Groovy和Java开发了一个应用程序。在我编写的所有代码中,都使用了相当多的静态元素。高级技术人员要求我减少使用的静态数据的数量。我在谷歌上搜索过相同的内容,我发现许多程序员都相当反对使用静态变量。

我发现静态变量使用起来更方便。而且我假定它们也是有效的(如果我错了请纠正我),因为如果我必须在一个类中对一个函数进行10,000次调用,我将很高兴使方法静态,并在其上使用直接的class . methodcall(),而不是用10,000个类实例使内存混乱,对吗?

此外,静态减少了对代码其他部分的相互依赖。他们可以作为完美的国家持有者。除此之外,我还发现一些语言(如Smalltalk和Scala)广泛地实现了静态。那么为什么程序员普遍反对静态(尤其是在Java世界中)呢?

PS:如果我对静态数据的假设是错误的,请纠正我。


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

我还可以继续讲很久,但更重要的概念是,事物的范围越窄,就越容易进行推理。我们擅长思考小事,但如果没有模块化,就很难推断出百万行系统的状态。顺便说一下,这适用于各种各样的东西——不仅仅是静态变量。


邪恶是一个主观的术语。

你不能从创建和破坏的角度来控制静态。他们按照程序的要求生活装卸。

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

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


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

线程安全——静态资源根据定义不是线程安全的 代码隐式——你不知道一个静态变量何时被实例化,也不知道它是否会在另一个静态变量之前被实例化


如果我必须在一个类中对一个函数进行10,000次调用,我会 很高兴使方法静态和使用直接 使用class.methodCall()来代替内存中10,000的混乱 类的实例,对吧?

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

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

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

其他答案也为反对过度使用静态数据提供了很好的理由。


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

欲了解更多信息,请阅读这篇文章 谢谢。


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

它只是关于如何分离逻辑并给它一个好的位置。有时这证明使用静态方法是正确的,java.lang.Math就是一个很好的例子。我认为,当您将大多数类命名为XxxUtil或Xxxhelper时,您最好重新考虑您的设计。


Static variables are generally considered bad because they represent global state and are therefore much more difficult to reason about. In particular, they break the assumptions of object-oriented programming. In object-oriented programming, each object has its own state, represented by instance (non-static) variables. Static variables represent state across instances which can be much more difficult to unit test. This is mainly because it is more difficult to isolate changes to static variables to a single test.

也就是说,区分常规静态变量(通常被认为是不好的)和最终静态变量(AKA常量;还不错)。


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

我明白你的想法,但一个简单的单例模式将做同样的事情,而不必实例化10,000个对象。

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

ex:

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

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

不。全球国家本身并不邪恶。但我们必须看看你的代码,看看你是否正确使用了它。一个新手很有可能滥用全局状态;就像他会滥用每一种语言特性一样。

全局状态是绝对必要的。我们不能回避全球国家。我们无法避免对全球状态进行推理。-如果我们关心我们的应用程序语义。

那些为了摆脱全局状态而试图摆脱全局状态的人,最终不可避免地会得到一个更复杂的系统——全局状态仍然存在,巧妙地/愚蠢地伪装在许多层间接之下;在解开所有的间接之后,我们仍然需要推理全局状态。

就像Spring的人在xml中大量声明全局状态,并认为它在某种程度上更优越。

@Jon Skeet如果我创建一个对象的新实例,现在你有两件事要考虑——对象内部的状态,以及承载对象的环境的状态。


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

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


以上所有答案都说明了为什么静态方法很糟糕。它们是邪恶的原因是因为它给人一种错误的印象,即你在编写面向对象的代码,而事实上你并不是。 这简直是邪恶。


a)关于程序的原因。

如果你有一个小型到中型的程序,其中静态变量Global。Foo被访问,对它的调用通常不知道从哪里来——没有路径,因此也没有时间轴,变量是如何到达这个地方的,它在哪里被使用。现在我怎么知道是谁设置了它的实际值呢?如果我现在修改它,我怎么知道会发生什么呢?我让grep覆盖整个源代码,收集所有访问,知道发生了什么。

如果你知道如何使用它,因为你只是写了代码,问题是看不见的,但如果你试着理解外国代码,你就会明白。

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

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

只有那些在较长时间内不会被很多人密集使用的或多或少无用的代码才可能适合使用静态变量。


因为没有人*提到它:并发。如果有多个线程读取和写入静态变量,那么静态变量可能会让您感到惊讶。这在web应用程序(例如ASP.NET)中很常见,它会导致一些相当令人恼火的错误。例如,如果有一个静态变量由页面更新,并且页面由两个人“几乎同时”请求,一个用户可能会得到另一个用户所期望的结果,甚至更糟。

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

我希望您已经准备好使用锁和处理争用。

*事实上,Preet Sangha提到过。


“静态是邪恶的”这个问题更多的是关于全局状态的问题。一个变量保持静态的适当时间是当它从来没有超过一个状态时;IE工具应该可以被整个框架访问,并且总是为相同的方法调用返回相同的结果,这些都不是静态的“邪恶”。关于你的评论:

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

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

The problem with global state is the inherent inconsistency that it can create. Documentation about unit tests often address this issue, since any time there is a global state that can be accessed by more than multiple unrelated objects, your unit tests will be incomplete, and not 'unit' grained. As mentioned in this article about global state and singletons, if object A and B are unrelated (as in one is not expressly given reference to another), then A should not be able to affect the state of B.

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


还有一个原因:脆弱性。

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

您可以记录这种情况,或者防止这种情况发生(单例/工厂模式)——但这是额外的工作,因此需要额外的成本。 即使这样,在大公司中,也有可能有人在某些时候尝试使用您的类,而没有完全注意到所有好的评论或工厂。

如果你经常使用静态变量,它就会崩溃。虫子是昂贵的。

在0.0001%的性能改进和可能不了解的开发人员更改的健壮性之间,在很多情况下健壮性是不错的选择。


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


Its not very object oriented: One reason statics might be considered "evil" by some people is they are contrary the object-oriented paradigm. In particular, it violates the principle that data is encapsulated in objects (that can be extended, information hiding, etc). Statics, in the way you are describing using them, are essentially to use them as a global variable to avoid dealing with issues like scope. However, global variables is one of the defining characteristics of procedural or imperative programming paradigm, not a characteristic of "good" object oriented code. This is not to say the procedural paradigm is bad, but I get the impression your supervisor expects you to be writing "good object oriented code" and you're really wanting to write "good procedural code".

在Java中,当您开始使用静态时,有许多并不总是立即明显的陷阱。例如,如果在同一个VM中运行两个程序副本,它们会共享静态变量的值并混淆彼此的状态吗?或者当你扩展类时会发生什么,你能重写静态成员吗?您的虚拟机内存不足,因为您有疯狂的静态数据,并且内存不能为其他需要的实例对象回收?

Object Lifetime: Additionally, statics have a lifetime that matches the entire runtime of the program. This means, even once you're done using your class, the memory from all those static variables cannot be garbage collected. If, for example, instead, you made your variables non-static, and in your main() function you made a single instance of your class, and then asked your class to execute a particular function 10,000 times, once those 10,000 calls were done, and you delete your references to the single instance, all your static variables could be garbage collected and reused.

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

Other Options: If efficiency is your primary concern, there might be other better ways to solve the speed problem than considering only the advantage of invocation being usually faster than creation. Consider whether the transient or volatile modifiers are needed anywhere. To preserve the ability to be inlined, a method could be marked as final instead of static. Method parameters and other variables can be marked final to permit certain compiler optimiazations based on assumptions about what can change those variables. An instance object could be reused multiple times rather than creating a new instance each time. There may be compliler optimization switches that should be turned on for the app in general. Perhaps, the design should be set up so that the 10,000 runs can be multi-threaded and take advantage of multi-processor cores. If portablity isn't a concern, maybe a native method would get you better speed than your statics do.

If for some reason you do not want multiple copies of an object, the singleton design pattern, has advantages over static objects, such as thread-safety (presuming your singleton is coded well), permitting lazy-initialization, guaranteeing the object has been properly initialized when it is used, sub-classing, advantages in testing and refactoring your code, not to mention, if at some point you change your mind about only wanting one instance of an object it is MUCH easier to remove the code to prevent duplicate instances than it is to refactor all your static variable code to use instance variables. I've had to do that before, its not fun, and you end up having to edit a lot more classes, which increases your risk of introducing new bugs...so much better to set things up "right" the first time, even if it seems like it has its disadvantages. For me, the re-work required should you decide down the road you need multiple copies of something is probably one of most compelling reasons to use statics as infrequently as possible. And thus I would also disagree with your statement that statics reduce inter-dependencies, I think you will end up with code that is more coupled if you have lots of statics that can be directly accessed, rather than an object that "knows how to do something" on itself.


我的美元。这些答案中有几个混淆了这个问题,而不是说“静态是坏的”,我认为更好的是谈论范围和实例。

我想说的是,静态变量是一个“类”变量——它表示一个值,该值在该类的所有实例中共享。通常情况下,它也应该以这种方式确定作用域(对类及其实例进行保护或私有)。

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

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

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

并发性对于静态方法的危险并不比实例方法小。它仍然是需要保护的州。当然,你可能有1000个实例,每个实例变量只有一个静态变量,但如果访问它们的代码不是以线程安全的方式编写的,你仍然会被搞砸——只是你可能需要更长的时间才能意识到这一点。

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

PS,就Smalltalk而言,它的许多方言确实有类变量,但在Smalltalk中,类实际上是元类的实例,所以它们实际上是元类实例上的变量。尽管如此,我还是会运用同样的经验法则。如果它们被用于跨实例的共享状态,那么ok。如果它们支持公共功能,你应该考虑单例。唉,我真的很想念Smalltalk....


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

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

静态方法又名实用方法。使用它们通常不是一个坏的做法,但主要的问题是它们可能阻碍测试。

作为一个伟大的java项目的例子,使用了大量的静态和做它的正确方式,请看看Play!框架。在SO中也有关于它的讨论。

与静态导入相结合的静态变量/方法也被广泛应用于方便java中声明性编程的库中,如:make it easy或Hamcrest。如果没有大量的静态变量和方法,这是不可能的。

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


如果你使用“static”关键字而没有使用“final”关键字,这应该是一个信号,提醒你仔细考虑你的设计。即使“final”的存在也不是免费的,因为可变的静态final对象可能同样危险。

我估计在85%的情况下,我看到的是没有“final”的“static”,这是错误的。我经常会发现一些奇怪的方法来掩盖或隐藏这些问题。

请不要创建静态变量。特别是集合。一般来说,集合应该在其包含对象初始化时初始化,并且应该设计为当其包含对象被遗忘时重置或忘记集合。

使用静态方法可能会产生非常细微的错误,这会让工程师痛苦数日。我知道,因为这些虫子都是我创造和猎杀的。

如果你想了解更多细节,请阅读…

为什么不使用静态方法?

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

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

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

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

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

一旦你这样做了,你就可以在CustomerFilterTest中快速模拟或剔除一个备用DAO,允许你对测试有更多的控制,

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

执行测试

Statics cause a real problem when running suites of unit tests together (for example, with your Continuous Integration server). Imagine a static map of network Socket objects that remains open from one test to another. The first test might open a Socket on port 8080, but you forgot to clear out the Map when the test gets torn down. Now when a second test launches, it is likely to crash when it tries to create a new Socket for port 8080, since the port is still occupied. Imagine also that Socket references in your static Collection are not removed, and (with the exception of WeakHashMap) are never eligible to be garbage collected, causing a memory leak.

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

这些问题经常出现在框架对象中,例如,您的DB访问、缓存、消息传递和日志记录层。如果您正在使用Java EE或一些最好的框架,它们可能会为您管理很多这方面的工作,但如果您像我一样正在处理遗留系统,则可能会有许多自定义框架来访问这些层。

如果应用于这些框架组件的系统配置在单元测试之间发生了变化,并且单元测试框架没有拆除并重新构建组件,那么这些变化就不能生效,并且当测试依赖于这些变化时,它们就会失败。

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

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

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

细微的错误

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

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

When a thread dies, a static object doesn’t get reset or garbage collected. Imagine you have a thread called “EmailCustomers”, and when it starts it populates a static String collection with a list of email addresses, then begins emailing each of the addresses. Lets say the thread is interrupted or canceled somehow, so your high availability framework restarts the thread. Then when the thread starts up, it reloads the list of customers. But because the collection is static, it might retain the list of email addresses from the previous collection. Now some customers might get duplicate emails.

题外话:静态决赛

“static final”的使用实际上相当于Java中的c#定义,尽管在技术实现上存在差异。C/ c++ #定义在编译前被预处理程序从代码中交换出来。Java的“静态final”将最终驻留在JVM的类内存中,使其(通常)永久存在于ram中。这样,它更类似于c++中的“静态const”变量,而不是#define变量。

总结

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


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

First, about static variables. Static variables are completelly unnecesary and it's use can be avoided easily. In OOP languajes in general, and in Java particularlly, function parameters are pased by reference, this is to say, if you pass an object to a funciont, you are passing a pointer to the object, so you dont need to define static variables since you can pass a pointer to the object to any scope that needs this information. Even if this implies that yo will fill your memory with pointers, this will not necesary represent a poor performance because actual memory pagging systems are optimized to handle with this, and they will maintain in memory the pages referenced by the pointers you passed to the new scope; usage of static variables may cause the system to load the memory page where they are stored when they need to be accessed (this will happen if the page has not been accesed in a long time). A good practice is to put all that static stuf together in some little "configuration clases", this will ensure the system puts it all in the same memory page.

Second, about static methods. Static methods are not so bad, but they can quickly reduce performance. For example, think about a method that compares two objects of a class and returns a value indicating which of the objects is bigger (tipical comparison method) this method can be static or not, but when invoking it the non static form will be more eficient since it will have to solve only two references (one for each object) face to the three references that will have to solve the static version of the same method (one for the class plus two, one for each object). But as I say, this is not so bad, if we take a look at the Math class, we can find a lot of math functions defined as static methods. This is really more eficient than putting all these methods in the class defining the numbers, because most of them are rarelly used and including all of them in the number class will cause the class to be very complex and consume a lot of resources unnecesarilly.

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

PS:抱歉我的英语不好。


静态变量本身并没有什么问题。只是Java语法有问题。每个Java类实际上定义了两个结构——一个封装静态变量的单例对象和一个实例。在同一个源代码块中定义这两种代码是非常糟糕的,会导致代码难以阅读。Scala做得很好。


假设您有一个拥有许多用户的应用程序,并且您已经定义了一个静态函数,该函数将状态保存在一个静态变量中,那么每个用户都将修改其他用户的状态。


这里有很多很好的答案,

记忆: 静态变量只要类加载器存在(通常直到VM死亡)就存在,但这只是在大块对象/引用存储为静态的情况下。

模块化: 考虑IOC、依赖注入、代理等概念。所有这些都完全反对紧密耦合/静态实现。

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


我只是对回答中的一些观点进行了总结。如果你发现任何错误,请随时改正。

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

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

测试:虽然可测试的设计并不等于好的设计,但我们很少会看到一个好的设计是不可测试的。由于静态变量代表全局状态,因此测试它们非常困难。

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

序列化:序列化也不能很好地与它们一起工作。

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

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

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

同样,如果静态变量被标记为final,它就变成了一个常量,赋给它一次的值就不能改变了。这意味着它将把我们从由于其可变性而面临的所有问题中拯救出来。


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

优点:

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

缺点:

Static members are always part of memory whether they are in use or not. You can not control creation and destruction of static variable. Usefully they have been created at program loading and destroyed when program unload (or when JVM shuts down). You can make statics thread safe using synchronize but you need some extra efforts. If one thread change value of a static variable that can possibly break functionality of other threads. You must know “static“ before using it. You cannot override static methods. Serialization doesn't work well with them. They don't participate in runtime polymorphism. There is a memory issue (to some extent but not much I guess) if a large number of static variables/methods are used. Because they will not be Garbage Collected until program ends. Static methods are hard to test too.


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

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


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


从我的观点来看,静态变量应该只读取数据或变量创建的约定。

For example we have a ui of some project, and we have a list of countries, languages, user roles, etc. And we have class to organize this data. we absolutely sure that app will not work without this lists. so the first that we do on app init is checking this list for updates and getting this list from api (if needed). So we agree that this data is "always" present in app. It is practically read only data so we don't need to take care of it's state - thinking about this case we really don't want to have a lot of instances of those data - this case looks a perfect candidate to be static.


我玩过很多静力学,我可以给你一个稍微不同的答案——或者可能是稍微不同的看待它的方式吗?

当我在一个类中使用静态(成员和方法)时,我最终开始注意到我的类实际上是两个分担责任的类——“静态”部分很像单例,而非静态部分(普通类)。据我所知,你可以通过为一个类选择所有静态类,为另一个类选择所有非静态类来完全分离这两个类。

当我在一个类中有一个静态集合,其中包含类的实例和一些静态方法来管理集合时,这种情况经常发生。一旦你思考一下,很明显你的类不是在做“一件事”,它是一个集合,做一些完全不同的事情。

现在,让我们稍微重构一下这个问题:如果你把你的类分成一个类,其中所有的东西都是静态的,而另一个类只是一个“普通类”,然后忘记“普通类”,那么你的问题就变成了纯静态类vs单例,这是在这里详细讨论的(可能还有其他十几个问题)。


Static fields are de facto GC roots (see the How Garbage Collection Works section earlier in this chapter), which means they are never garbage-collected! For convenience alone, static fields and collections are often used to hold caches or share state across threads. Mutable static fields need to be cleaned up explicitly. If the developer does not consider every possibility (a near certainty), the cleanup will not take place, resulting in a memory leak. This sort of careless programming means that static fields and collections have become the most common cause of memory leaks!

简而言之,永远不要使用可变静态字段——只使用常量。如果您认为需要可变静态字段,请再三考虑!总有更合适的方法。