在Java中,类的静态方法有什么缺点吗?

Mic*_*ell 36 java static-methods runtime

让我们假设在我的编码环境中强加了一条规则(或者经验法则),即不使用,修改或以其他方式需要任何实例变量来完成其工作的类上的任何方法都是静态的.这样做有没有固有的编译时间,运行时或任何其他缺点?

(编辑进一步澄清)

我知道这个问题有点开放和模糊,所以我为此道歉.我的提问意图主要是"辅助"方法.实用程序类(具有私有CTOR,因此无法实例化)作为我们已经执行的静态方法的持有者.我在这里的问题更像是帮助主类API的这些小方法.

我可能在一个完成实际工作的类上有4或5个主要的API /实例方法,但在这样做的过程中,它们共享一些常用功能,这些功能可能只用于API方法的输入参数,而不是内部状态.这些是我通常在他们自己的帮助器方法中提取的代码部分,如果他们不需要访问类的状态,那么将它们设置为静态.

我的问题是,这本质上是一个坏主意,如果是这样,为什么?(或者为什么不呢?)

che*_*vim 28

主要缺点是您无法在运行时交换,覆盖或选择方法实现.

  • +1即使方法不需要任何对象状态,它仍然可以依赖于对象类型,但前提是它不是*`static`. (2认同)

Eri*_*zzo 25

在我看来,有四个原因可以避免Java中的静态方法.这并不是说静态方法永远不适用,只是说它们通常应该避免使用.

  1. 正如其他人所指出的那样,静态方法不能在单元测试中被模拟出来.如果一个类依赖于,DatabaseUtils.createConnection()那么,那个依赖类,以及任何依赖它的类,几乎不可能在没有实际拥有数据库或某种"测试"标志的情况下进行测试DatabaseUtils.在后一种情况下,听起来你实际上有两个DatabaseConnectionProvider接口的实现- 请参阅下一点.

  2. 如果您有静态方法,则其行为适用于所有类,无处不在.有条件地改变其行为的唯一方法是将标志作为参数传递给方法或在某处设置静态标志.第一种方法的问题是它改变了每个调用者的签名,并且随着越来越多的标志被添加而迅速变得麻烦.第二种方法的问题是你最终会得到这样的代码:

    boolean oldFlag = MyUtils.getFlag();
    MyUtils.someMethod();
    MyUtils.setFlag( oldFlag );
    

    遇到此问题的常见库的一个示例是Apache Commons Lang:请参阅StringUtilsBean等.

  3. 每个ClassLoader加载一次对象,这意味着你实际上可能在不知不觉中有静态方法和静态变量的多个副本,这可能会导致问题.这通常与实例方法无关,因为对象是短暂的.

  4. 如果您有静态方法引用静态变量,那些方法会在类加载器的生命周期中保留,并且永远不会收集垃圾.如果这些累积信息(例如缓存)并且您不小心,则可能会在应用程序中遇到"内存泄漏".如果您使用实例方法,则对象往往寿命较短,因此在一段时间后会进行垃圾回收.当然,您仍然可以使用实例方法进入内存泄漏!但这不是一个问题.

希望有所帮助!

  • 第 4 点是我听过的不使用静态方法的最佳理由......尽管我仍然不相信:-) (2认同)
  • 使用“PowerMockito”的 JIC 可以模拟“静态方法”。 (2认同)

Ste*_* B. 17

性能优势可能微不足道.对任何不依赖于状态的东西使用静态方法.这澄清了代码,因为您可以立即通过静态方法调用看到没有涉及实例状态.

  • 通常,它可以使单元测试变得更加困难,特别是遗留代码不是以可以进行单元测试的方式编写的. (4认同)
  • @Yishai你能澄清一下吗?如果该方法是静态的,则其输出仅由其输入确定,而不是由任何外部状态确定.该约束如何使单元测试更加困难? (4认同)
  • Yishai,调用Web服务或数据库不是独立于国家的.在DI框架中,这些是注入单身的依赖关系,并且是单身人士状态的一部分.我不认为那些将用静力学正确建模. (4认同)
  • @Callahad,通过静态方法不能被模拟或抽象.因此,如果该静态方法调用Web服务,命中数据库,启动线程或执行任何不可单元测试的类似操作,则会使模拟,抽象或以其他方式将方法重定向到测试中的其他内容变得更加困难. (2认同)
  • @Callahad曾经试图嘲笑一个单身人士?静态方法不能被虚拟实现轻松替换(尽管可以使用高级模拟框架) (2认同)

Faz*_*zal 7

我真的很喜欢这个问题,因为这是我在职业生涯中过去4年一直在争论的一个问题.对于没有任何状态的类,静态方法很有意义.但最近我有点修改过我.

具有静态方法的实用程序类是个好主意.

在许多情况下,承载业务逻辑的服务类可以是无状态的.最初我总是在其中添加静态方法,但是当我更熟悉Spring框架(以及更一般的阅读)时,我意识到这些方法作为一个独立的单元变得不可测试,因为你不能轻易地将模拟服务注入到这个类中.例如,静态方法在另一个类中调用另一个静态方法,JUnit测试无法通过在运行时注入虚拟实现来短路tis路径.

所以我有点认为,实用的静态方法几乎不需要调用其他类或方法就可以是静态的.但是服务类通常应该是非静态的.这允许您利用覆盖等OOP功能.

还有一个单例实例类可以帮助我们创建一个非常像静态类的类仍然使用OOP概念.

  • 由于只应对旨在被重写的方法进行重写,所以我不认为这是使所有可能的方法变为非静态的驱动原因。就个人而言,差异可以忽略不计,尤其是对于私有代码而言,因此更好的理由是减少了键入。:) (2认同)

Ree*_*nda 6

Disadvantage -> Static

成员是类的一部分,因此保留在内存中直到应用程序终止.并且不能被垃圾收集.使用过多的静态成员有时会预测您无法设计产品并尝试使用静态/过程编程.它表示面向对象的设计受到损害.这可能导致内存过流.