【问题标题】:Writing to a static variable in an instance method, why is this a bad practice?在实例方法中写入静态变量,为什么这是一个不好的做法?
【发布时间】:2011-06-19 15:12:08
【问题描述】:

我对 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";
   }
}

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

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

【问题讨论】:

    标签: java methods static instance


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

      并且 findbugs 不够聪明,无法找出您的实例方法间接更改了第二个示例中的字段:)

      【讨论】:

        【解决方案3】:
        【解决方案4】:

        它是一种别名形式,可能违反直觉。违反直觉的代码妨碍了维护。

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

        让我们将doSomething 重命名为initialize

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

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

        但是,代码是:

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

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

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


        对于你的最后一个例子,

        • 实例调用静态方法

          实例方法正在调用静态方法这一事实不应引发标志。这些例子表明这很有用,远远超过了它可能存在的问题。

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

          从某种意义上说,它应该产生一个不同但相似的警告:一个类正在弄乱另一个类的数据。但是,通过将静态变量设为 public 是一种默认的方式,因此不需要这样的警告。

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

        【讨论】:

        • 我喜欢你强调易于理解和清晰作为基本原理而不是同步(无论方法是否是静态的,这都是有问题的)。
        【解决方案5】:

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

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

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

        【讨论】:

          【解决方案6】:

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

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

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-01-12
            • 1970-01-01
            • 2011-06-01
            • 2010-10-26
            • 2011-07-07
            • 1970-01-01
            • 2010-09-30
            • 2013-08-18
            相关资源
            最近更新 更多