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

时间:2009-11-19 21:32:14

标签: java static static-methods

什么时候在方法签名上使用Java中的static关键字被认为是不好的做法?如果方法基于某些参数执行函数,并且不需要访问非静态字段,那么您是否总是希望这些类型的方法是静态的?

9 个答案:

答案 0 :(得分:46)

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

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

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

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

More blahblah about pure functions(自我链接)以及为什么要坚持使用它们。

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

答案 1 :(得分:31)

可能不希望它是静态的一个原因是允许它在子类中被覆盖。换句话说,行为可能不依赖于对象内的数据,而是取决于对象的确切类型。例如,您可能有一个常规集合类型,其isReadOnly属性将在always-mutable集合中返回false,在始终不可变集合中返回true,并依赖于实例变量in其他

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

答案 2 :(得分:23)

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

  1. 静态方法使测试变得困难,因为它们无法替换,
  2. 静态方法更具程序性。
  3. 在我看来,静态方法适用于实用程序类(如StringUtils),但我更愿意尽量避免使用它们。

答案 3 :(得分:4)

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

例如,请考虑以下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.
    }
}

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

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

答案 4 :(得分:1)

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

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

  1. 它们使代码更少模块化,更难以测试/扩展。大多数答案已经解决了,所以我不再讨论了。
  2. 静态方法往往会导致某种形式的全局状态,这通常是导致潜在错误的原因。这可能发生在为上述第二个目的而编写的编写不良的代码中。让我详细说明。
  3. 例如,考虑一个需要将某些事件记录到数据库的项目,并依赖于其他状态的数据库连接。假设通常首先初始化数据库连接,然后将日志记录框架配置为将某些日志事件写入数据库。现在假设开发人员决定从手写的数据库框架转移到现有的数据库框架,例如hibernate。

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

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

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

一个小小的抱怨,但的另一件事就是关于无偿的静态函数,呃,方法。

答案 8 :(得分:0)

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