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

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

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

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


当前回答

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.

其他回答

如果你使用“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框架,您可能不会遇到很多这样的情况,但如果您正在处理大量遗留代码,它们可能会变得更加频繁。

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

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.

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

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

这里有很多很好的答案,

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

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

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