繁体   English   中英

什么时候不要在 Java 中使用 static 关键字?

[英]When NOT to use the static keyword in Java?

什么时候在方法签名上使用 Java 中的 static 关键字被认为是不好的做法? If a method performs a function based upon some arguments, and does not require access to fields that are not static, then wouldn't you always want these types of methods to be static?

您将在大型Java应用程序中遇到的两个最大的弊端是

  • 静态方法,除了那些纯函数*
  • 可变静态字段

这些破坏了代码的模块性,可扩展性和可测试性,我意识到我无法在这个有限的时间和空间中说服你。

*“纯函数”是任何不修改任何状态的方法,其结果仅取决于提供给它的参数。 因此,例如,任何执行I / O(直接或间接)的函数都不是纯函数,但Math.sqrt()当然是。

关于纯粹功能 (自我链接)的更多blahblah以及为什么要坚持它们。

我强烈建议您支持“依赖注入”编程风格,可能由Spring或Guice等框架支持(免责声明:我是后者的合着者)。 如果你这样做的权利,你将永远不会实质上需要可变静止状态还是非纯静态方法。

可能不希望它是静态的一个原因是允许它在子类中被覆盖。 换句话说,行为可能不依赖于对象内的数据,而是取决于对象的确切类型。 例如,你可能有一个通用的集合类型,与isReadOnly财产将返回false的永远可变集合, true的永远不变的集合,而在其他依赖于实例变量。

但是,根据我的经验,这种情况非常罕见 - 通常应明确说明。 通常我会创建一个不依赖于任何对象状态静态的方法。

一般来说,我更喜欢实例方法,原因如下:

  1. 静态方法使测试变得困难,因为它们无法替换,
  2. 静态方法更加面向程序。

在我看来,静态方法对于实用程序类(如StringUtils )是可以的,但我更愿意尽量避免使用它们。

你说的是真的,但是当你想要在派生类中覆盖该方法的行为时会发生什么? 如果它是静态的,你不能这样做。

作为示例,请考虑以下DAO类型类:

class CustomerDAO {
    public void CreateCustomer( Connection dbConn, Customer c ) {
       // Some implementation, created a prepared statement, inserts the customer record.
    }

    public Customer GetCustomerByID( Connection dbConn, int customerId ) {
       // Implementation
    }
}

现在,这些方法都不需要任何“状态”。 他们需要的一切都作为参数传递。 所以他们很容易变得静止。 现在要求你需要支持一个不同的数据库(比如说Oracle)

由于这些方法不是静态的,您可以创建一个新的DAO类:

class OracleCustomerDAO : CustomerDAO {
    public void CreateCustomer( Connection dbConn, Customer c ) {
        // Oracle specific implementation here.
    }

    public Customer GetCustomerByID( Connection dbConn, int customerId ) {
        // Oracle specific implementation here.
    }
}

现在可以使用这个新类来代替旧类。 如果您正在使用依赖注入,它甚至可能根本不需要更改代码。

但是如果我们将这些方法设置为静态,那么事情会变得更复杂,因为我们不能简单地覆盖新类中的静态方法。

静态方法通常用于两个目的。 第一个目的是使用某种全局实用方法,类似于java.util.Collections中的功能。 这些静态方法通常是无害的。 第二个目的是通过各种设计模式(如单例工厂 )来控制对象实例化并限制对资源(例如数据库连接)的访问。 如果实施不当,这些可能会导致问题。

对我来说,使用静态方法有两个缺点:

  1. 它们使代码更少模块化,更难以测试/扩展。 大多数答案已经解决了这个问题,所以我不再讨论了。
  2. 静态方法往往会导致某种形式的全局状态,这通常是潜在的错误的原因。 这可能发生在为上述第二个目的而编写的编写不良的代码中。 让我详细说明一下。

例如,考虑一个需要将某些事件记录到数据库的项目,并依赖于其他状态的数据库连接。 假设通常首先初始化数据库连接,然后将日志记录框架配置为将某些日志事件写入数据库。 现在假设开发人员决定从手写的数据库框架转移到现有的数据库框架,例如hibernate。

但是,这个框架很可能有自己的日志记录配置 - 如果它恰好使用与您相同的日志框架,那么配置之间很可能会出现各种冲突。 突然之间,切换到不同的数据库框架会导致系统中看似无关的不同部分的错误和故障。 这种故障可能发生的原因是因为日志记录配置维护通过静态方法和变量访问的全局状态,并且系统的不同部分可以覆盖各种配置属性。

为了摆脱这些问题,开发人员应该避免通过静态方法和变量存储任何状态。 相反,他们应该构建干净的API,让用户根据需要管理和隔离状态。 BerkeleyDB就是一个很好的例子,通过Environment对象而不是静态调用封装状态。

那就对了。 实际上,您必须将可能是合理设计(将一些与类无关的函数)转换为Java术语。 这就是为什么你会看到像FredsSwingUtils和YetAnotherIOUtils这样的全能类。

如果要独立于该类的任何对象使用类成员,则应将其声明为static。
如果它被声明为static,则可以在没有该类对象的现有实例的情况下访问它。 静态成员由该特定类的所有对象共享。

关于静态方法的另一个烦恼是:没有简单的方法来传递对这样的函数的引用而不在它周围创建包装类。 例如 - 类似于:

FunctorInterface f = new FunctorInterface() { public int calc( int x) { return MyClass.calc( x); } };

我讨厌这种java make-work。 也许更高版本的java会获得委托或类似的函数指针/程序类型机制?

一个轻微的抱怨,但还有一件事喜欢无偿的静态功能,呃,方法。

这里有两个问题1)创建对象的静态方法在第一次访问时会保留在内存中吗? 这个(在内存中仍然加载)是一个缺点吗? 2)使用Java的一个优点是它的垃圾收集功能 - 当我们使用静态方法时,我们是否忽略了这一点?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM