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

时间:2011-02-01 23:05:58

标签: java methods static instance

我对这个在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,但是我正在从属性文件中加载值。

6 个答案:

答案 0 :(得分:18)

它是一种混叠形式,可能违反直觉。反直觉的代码阻碍了维护的简便性。

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

我们将doSomething重命名为initialize

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

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

然而,代码是:

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

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

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


对于你的上一个例子,

  • 实例调用静态方法

    实例方法调用静态方法的事实不会引发标志。这些例子远远超过它可能存在的问题。

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

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

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

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:3)

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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

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

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

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