首页 文章

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

提问于
浏览
551

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

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

此外,静态减少了代码其他部分的相互依赖性 . 他们可以充当完美的国家持有者 . 除此之外,我发现静态在一些语言中被广泛实现,如SmalltalkScala . 那么为什么程序员(特别是Java世界)中流行的静态压迫呢?

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

29 回答

  • 13

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

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

  • 5

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

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

  • 26

    Summarising few basic Advantages & Disadvantages of using Static methods in Java:

    Advantages:

    • 全球可访问,即不与任何特定对象实例绑定 .

    • 每个JVM一个实例 .

    • 可以使用类名访问(无需对象) .

    • 包含适用于所有实例的单个值 .

    • 加载JVM启动并在JVM关闭时死亡 .

    • 它们不会修改Object的状态 .

    Disadvantages:

    • 静态成员始终是他们正在使用或未使用的内存天气的一部分 .

    • 您无法控制静态变量的创建和销毁 . 有用的是,它们是在程序加载时创建的,并在程序卸载时(或当JVM关闭时)被销毁 .

    • 您可以使用同步使静态线程安全,但您需要额外的努力 .

    • 如果一个线程更改了可能破坏其他线程功能的静态变量的值 .

    • 使用前必须知道“静态” .

    • 您无法覆盖静态方法 .

    • 序列化对它们不起作用 .

    • 他们不参与运行时多态性 .

    • 如果使用大量静态变量/方法,则存在内存问题(在某种程度上,但我猜不多) . 因为在程序结束之前它们不会是GC .

    • 静态方法也很难测试 .

  • 5

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

  • 2

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

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

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

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

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

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

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

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

  • 8

    一切(可以:)有它的目的,如果你有一堆线程需要 share/cache 数据和 all accessible memory (所以你不要分成一个JVM中的上下文)静态是最好的选择

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

  • 54

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

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

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

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

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

    Why Not Use Statics?

    静态问题有很多问题,包括编写和执行测试,以及不是很明显的微妙错误 .

    依赖于静态对象的代码不能轻易地进行单元测试,并且通常不能轻易地模拟静态 .

    如果使用静态,则无法将类的实现交换出来以测试更高级别的组件 . 例如,假设一个静态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 .

    Executing Tests

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

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

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

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

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

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

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

    Subtle Bugs

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

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

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

    An Aside: Static Final

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

    Summary

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

  • 620

    可能会建议在大多数使用静态变量的情况下,你真的想要使用singleton pattern .

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

  • 4

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

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

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

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

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

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

  • 4

    a)关于节目的原因 .

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

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

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

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

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

  • 0

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

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

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

    *实际上,Preet Sangha提到了它 .

  • 1

    如果我必须对类中的函数进行10,000次调用,我很乐意将该方法设置为静态并在其上使用简单的class.methodCall()而不是使用10,000个类的实例来混乱内存,对吗?

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

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

    封装也是如此 . 在大型应用程序中,静态算法倾向于生成意大利面条代码,并且不容易进行重构或测试 .

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

  • 87

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

  • 3

    从我的观点来看 static 变量应该只是 read only 数据 or 变量创建 by convention .

    例如,我们有一个项目的ui,我们有一个国家,语言,用户角色等的列表 . 我们有一个类来组织这些数据 . 我们绝对相信,如果没有此列表,应用程序将无法运行所以我们在app init上做的第一件事就是检查这个列表的更新并从api获取这个列表(如果需要的话) . 所以我们同意这个数据是"always"存在于应用程序中 . 它实际上是只读数据,所以我们不要这样做 - 考虑这个案例,我们真的不希望有很多这些数据的实例 - 这个案例看起来很合适 static .

  • 3

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

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

  • 4

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

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

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

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

    PS:抱歉我的英文 .

  • 13

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

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

  • 6

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

  • 2

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

    全球国家是绝对必要的 . 我们无法避免全球国家 . 我们无法避免对全球国家的推理 . - 如果我们关心我们的应用程序语义 .

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

    就像 Spring 天的人们在xml中大肆宣布全球状态一样,并且认为它更优越 .

    @Jon Skeet if I create a new instance of an object 现在你有两件事要理由 - 对象内的状态,以及托管对象的环境状态 .

  • 11

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

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

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

    例如:

    public class WaterContainer {
        private int size;
        private int brand;
        ...etc
    
        public static int convertToGallon(int liters)...
    
        public static int convertToLiters(int gallon)...
    
    }
    
  • 4

    还有另一个原因:脆弱 .

    如果你有一个课程,大多数人都希望能够创建它并随意使用它 .

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

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

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

  • 9

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

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

    Statics are the ideal and efficient choice for variables/classes that do not ever change .

    全局状态的问题是它可以创建的固有的不一致性 . 有关单元测试的文档通常可以解决这个问题,因为任何时候都存在一个可以由多个不相关的对象访问的全局状态,您的单元测试将是不完整的,而不是_135078_粒度 . 正如本文中提到的关于global state and singletons,如果对象A和B不相关(如在一个中没有明确指出另一个),则A不应该能够影响B的状态 .

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

  • 0

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

  • 29

    邪恶是一个主观的术语 .

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

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

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

  • 0

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

    欲了解更多信息,请阅读this谢谢 .

  • 5

    Its not very object oriented: 某些人可能会认为静态的一个原因是它们与object-oriented paradigm相反 . 特别是,它违反了数据被封装在对象中的原则(可以扩展,信息隐藏等) . 静态,就像您描述使用它们的方式一样,本质上是将它们用作全局变量,以避免处理范围等问题 . 但是,全局变量是程序或命令式编程范例的定义特征之一,而不是面向对象代码的特征 . 这并不是说程序范式很糟糕,但我得到的印象是你的主管希望你写作"good object oriented code"并且你真的想写"good procedural code" .

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

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

    Prevents certain re-use: 此外,静态方法不能用于实现接口,因此静态方法可以防止某些面向对象的特性可用 .

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

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

  • 245

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

    • 线程安全 - 根据定义,静态资源不是线程安全的

    • Code Implicity - 您不知道何时实例化静态变量以及是否将在另一个静态变量之前实例化它

  • 5

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

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

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

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

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

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

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

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

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

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

  • 3

    这里有很多好的答案,加入其中,

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

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

    其他Con:螺纹安全性,可测性

相关问题