何时不使用Java中的static关键字?

什么时候在方法签名中使用Java中的static关键字被认为是不好的做法? 如果一个方法根据一些参数执行一个函数,并且不需要访问非静态的字段,那么你是否总是希望这些types的方法是静态的?

为什么你不希望它是静态的一个原因是让它在一个子类中被覆盖。 换句话说,行为可能不取决于对象内的数据,而取决于对象的确切types。 例如,您可能有一个通用集合types,其中isReadOnly属性将在always-mutable集合中返回false ,在always-immutable集合中返回false ,并依赖于其他集合中的实例variables。

但是,这在我的经验中是非常罕见的 – 通常应该明确说明。 通常我会做一个不依赖于任何对象状态静态的方法。

在大规模Java应用程序中遇到的两个最大的罪恶是

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

这破坏了你的代码的模块性,可扩展性和可testing性,我意识到我不可能希望在这个有限的时间和空间中使你相信。

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

更纯粹的functionblabblah (自我链接),为什么你要坚持他们。

我强烈build议你支持“dependency injection”编程风格,可能由Spring或Guice等框架支持(免责声明:我是后者的合着者)。 如果你这样做,你将永远不需要可变静态或非纯静态方法。

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

  1. 静态方法使得testing变得困难,因为它们不能被replace,
  2. 静态方法更加程序化。

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

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

作为一个例子,考虑下面的DAOtypes类:

 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 } } 

现在,这些方法都不需要任何“状态”。 他们所需要的一切都作为parameter passing。 所以他们可以很容易地变成静态的 现在需求来了,你需要支持一个不同的数据库(可以说Oracle)

由于这些方法不是静态的,你可以创build一个新的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. } } 

现在这个新class可以用来代替旧class。 如果您正在使用dependency injection,则甚至可能根本不需要更改代码。

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

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

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

  1. 他们使代码更less模块化,更难以testing/扩展。 大多数答案已经解决了这个问题,所以我不会再讨论这个问题了。
  2. 静态方法往往会导致某种forms的全局状态,这往往是造成隐患的原因。 这可能发生在为上述第二个目的而编写的写得不好的代码中。 让我详细说一下。

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

但是,这个框架很可能有自己的日志configuration – 如果碰巧使用与您的日志框架相同的日志框架,那么在configuration之间会有各种各样的冲突。 突然之间,切换到不同的数据库框架会导致系统不同部分的错误和失败,而这些错误和失败似乎是不相关的。 出现这种故障的原因是因为日志loggingconfiguration保持通过静态方法和variables访问的全局状态,各种configuration属性可以被系统的不同部分覆盖。

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

那就对了。 事实上,你必须扭转什么可能是一个合理的devise(有一些function不与一个类相关)到Java术语。 这就是为什么你看到所有的类,如FredsSwingUtils和YetAnotherIOUtils。

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

关于静态方法的一个额外的烦恼:没有简单的方法来传递这样一个函数的引用而不创build一个包装类。 例如 – 类似于:

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

我讨厌这种java的工作。 也许更高版本的Java会得到代表或类似的函数指针/程序types机制?

一个小小的抱怨,但还有一件事是喜欢关于无偿静态function,呃,方法。

这里有两个问题1)创build对象的静态方法在第一次访问时保持加载到内存中? 没有这个(剩余的在内存中加载)一个缺点? 2)使用Java的优点之一是它的垃圾回收function – 当我们使用静态方法时,我们是否忽略了这一点?