Java静态方法的优缺点

11

我以前没怎么使用静态方法,但最近我倾向于更多地使用它们。例如,如果我想在类中设置一个布尔标志或访问一个对象而不需要通过类传递实际对象。

例如:

public class MainLoop
{
    private static volatile boolean finished = false;

    public void run()
    {
        while ( !finished )
        {
            // Do stuff
        }

    }
    // Can be used to shut the application down from other classes, without having the actual object
    public static void endApplication()
    {
        MainLoop.finished = true;
    }

}

我应该避免这种做法吗?传递一个对象并使用对象的方法是否更好?布尔类型的 finished 现在算作全局变量了吗,还是仍然安全?


finished 变量确实是类的“全局”变量。Java 没有真正的“全局”(对于所有东西)变量。 - Hot Licks
1
@Hasslarn:你的代码出了问题(我有点惊讶,在四个答案之后,竟然没有人提到它):绝对不能保证另一个线程调用endApplication()时,该值将由运行run()方法的线程读取为true。你必须在这里使用一些同步机制,例如将finished声明为volatile - TacticalCoder
@Hasslarn:我编辑了你的问题并修复了你的代码(虽然这种方式使用静态不是很干净),通过在布尔标志上添加volatile - TacticalCoder
为你鼓掌,感谢你注意到了这一点,我只是临时编写了一个示例来阐述我的观点 :) - Lucas Arrefelt
4个回答

8
在这种情况下使用静态变量的问题在于,如果您创建了两个(或更多)MainLoop实例,并编写看起来只关闭其中一个实例的代码,实际上将关闭它们全部:
MainLoop mainLoop1 = new MainLoop();
MainLoop mainLoop2 = new MainLoop();

new Thread(mainLoop1).start();
new Thread(mainLoop2).start();

mainLoop1.finished = true; // static variable also shuts down mainLoop2 

这只是不使用静态变量的众多原因之一。即使今天您的程序只创建一个MainLoop,将来可能会有很多原因需要创建多个MainLoop:用于单元测试或实现新功能。您可能会认为“如果发生这种情况,我只需重构程序以使用成员变量而不是静态变量。”但通常最好一开始就在程序中付出成本,并将模块化设计融入其中。毫无疑问,静态变量经常使得编写快速且简单的程序更容易。但对于重要/复杂的代码,您打算进行测试、维护、扩展、共享并长期使用,通常建议不要使用静态变量。
作为对这个问题的其他答案所指出的,静态变量是一种全局变量。有很多关于为什么(通常)全局变量不好的信息。请参考链接1、链接2、链接3和链接4。

7

是的,传递对象更好。使用单例或静态方法会使面向对象编程看起来像过程式编程。单例略微好一些,因为您至少可以使其实现接口或扩展抽象类,但通常这是一种设计上的问题。

而像您所做的混合实例方法和静态变量更加令人困惑:您可能会有多个对象循环,但当静态变量更改时,您会立即停止所有对象的循环。


5

这是我应该避免的吗?

一般来说,是的。静态方法表示全局状态。全局状态很难理解,难以单独测试,并且通常具有更高的线程安全性要求。

如果我想测试某个对象处于某种状态时会发生什么,我只需要创建对象,将其置于该状态,执行我的测试,然后让它被垃圾回收即可。

如果我想测试发生在全局状态下会发生什么,我需要确保在测试结束时(或可能在每个测试开始时)重置所有状态。如果不小心处理,测试现在会相互干扰。

当然,如果静态方法不需要影响任何状态-即如果它是纯粹的-那么就会变得更好。此时,您失去的只是替换该方法实现的能力,例如当测试调用它时。


2
通常情况下,通过将finished声明为静态变量,您可以创建这样一种情况:在任何时候只能有一个MainLoop类的实例执行run。如果有多个实例,则设置finished将结束它们所有--这不是通常想要的结果。
然而,在这种特定的情况下,您想要“结束应用程序”,假设您想要终止所有MainLoop实例,这种方法可能是合理的。
然而,适用此方法的情况很少,更“干净”的处理此场景的方式是保持一个实例的静态列表,并通过列表处理每个实例,将实例变量finished设置在每个实例中。这使您也可以结束单个实例,为您提供现有实例的自然计数等。

是的,在这种特定情况下,我个人认为这是合理的,但正如你和其他人所暗示的那样,它可能会使扩展相同基础变得困难。感谢解决问题的示例。 - Lucas Arrefelt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接