在实例方法中写入静态变量,为什么这是一个不好的做法?

我在这里用eclipse中的这个findbugs警告有点困惑。

public class MyClass { public static String myString; } public class AnotherClass { public void doSomething() { MyClass.myString = "something"; } } 

这给了我一个findbugs警告“从实例方法写入静态字段”,但这并没有给我一个警告:

 public class MyClass { public static String myString; } public class AnotherClass { public void doSomething() { doAnotherThing(); } public static doAnotherThing() { MyClass.myString = "something"; } } 

这有什么不同?,为什么从实例方法写入静态变量是一种不好的做法?我认为它与同步有关,但我仍然不清楚。

我知道这看起来变量应该是final,但我从属性文件加载值。

它是一种混叠forms,可能违反直觉。 反直觉代码妨碍了易于维护。

从逻辑上讲,我们希望实例方法能够影响该实例的数据。 我们期望静态方法影响静态数据。

让我们重命名doSomethinginitialize

 ... a.initialize(); ... b.initialize(); ... 

此代码的读者可能不会立即意识到ab的实例实际上影响相同的数据。 这可能是一个错误,因为我们正在初始化相同的内存两次,但它不明显,因为我们可能需要在每个实例上调用initialize似乎是合理的。

但是,代码是:

 ... MyClass.initialize(); ... MyClass.initialize(); ... 

在这种情况下,它更直观,我们可能会影响相同的静态数据,这可能是一个错误。

这类似于别名的通用版本,其中同一范围内的两个变量指向同一实例。


对于你的上一个例子,

  • 实例调用静态方法

    实例方法调用静态方法的事实不会引发标志。 这个例子很有用,远远超过可能出现问题的地方。

  • 一个类的静态方法会影响另一个类的静态数据

    从某种意义上说,它应该生成一个不同但相似的警告:一个类正在弄乱另一个类的数据。 但是,通过使静态变量公开是一种默认的方式,所以不需要这样的警告。

请记住,FindBugs只是试图在代码中标记潜在的可能问题,而不是每个可能的问题。 您的第一个示例可能是一个潜在的维护问题,您需要检查它是否是一个真正的问题。 你的第二个例子可能不是问题,或者它是一个真正的问题,它与不是问题的用例过于相似。

您想要更改静态字段的原因并不多。 请记住,如果将此字段设置为新值,则此值已针对此类的所有实例进行了更改。 这可能会让您在multithreading环境中遇到麻烦,其中多个线程正在调用doSomething() 。 需要适当的同步。

在99%的情况下,您希望实例方法仅更改非静态字段,这就是findbugs警告您的原因。

并且findbugs不够聪明,无法找到你的实例方法在第二个例子中间接改变字段:)

这就是FindBugs对此所说的: http : //findbugs.sourceforge.net/bugDescriptions.html#ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD

这是我的看法,所以带上一粒盐。 您提到同步问题,这是此警告的主要原因,但更重要的是,这两个案例基本上是在不同的概念“级别”数据上运行。 实例方法由对象“拥有”并修改描述各个实例的数据。 类方法是通用操作和状态,虽然与类相关,但与单个对象无关。 因此,从每个实例中修改该状态可能(但不一定)是一个糟糕的设计决策。

因为更改静态字段会更改所有实例,如果未正确同步,则会导致无法解决的问题。

如果您正在读取属性文件以设置共享字段,则使用静态方法执行此操作。 或者,将字段重构为单独的单例实例,而另一个类只能读取。 如果您只有一个实例,则使用单例模式并使字段为非静态。

静态方法应该只影响静态数据,而实例方法应该只影响实例数据。

我不认为同步(在几个答案中提到)与此有任何关系。 毕竟,可以从多个线程调用静态方法,就像实例方法一样容易。

我认为,警告的原因(FindBugs文档没有很好地解释)是由几个答案暗示的:它是可疑的,可能是一个错误。 就像Jochen Bedersdorfer所说,并不是所有的用例都要从一个实例方法中分配给一个类中的静态变量。 就像

 while (x = y) { // ... } 

从技术上讲,这不是一个错误(如果x和y是布尔值,实际上是合法的Java),它几乎总是一个错误。 同样,FindBug的作者对主题案例也有同样的看法。