Java:为什么不声明静态而不是传递引用?

时间:2013-10-27 11:58:18

标签: java parameters constructor static

假设我们想制作一个你必须收集宝石的游戏。所以我们需要一个Gem类,一个GemSpawner类,当然还有MainActivity类。

public class MainActivity {
    public static void main(String[] args) {

        List<Gem> gems = new ArrayList<Gem>();

        GemSpawner gs = new GemSpawner(gems);

        //...
    }
}

在这种情况下,我们使用gems创建了一个List,并通过它的构造函数将其传递给GemSpawner,因此gs可以使用以下命令将gems添加到列表中:

gems.add(new Gem(10, 50, "red")); //should represent Xpos, Ypos, and color.

但这不会更好:

public class MainActivity {

    public static List<Gem> gems = new ArrayList<Gem>();

    public static void main(String[] args) {

        GemSpawner gs = new GemSpawner();

        //...
    }
}

现在,GemSpawner(gs)可以使用:

添加宝石
MainActivity.gems.add(new Gem(10, 50, "red"));

朋友只向我展示并解释了上述方法,但下面的方法不是更有效吗?

5 个答案:

答案 0 :(得分:7)

效率论证并不含水。它是一个单一的引用,所以32位(如果你在64位机器上,64位)在开始时被解析一次,所以它没有任何(可测量的)性能影响。

但是,如果使用大量静态函数,代码的设计可能会变得混乱。例如,通过使其静态,没有简单的方法来跟踪什么是添加到您的列表的东西(可能是一个晚上测试你随机添加宝石然后忘记的东西。几个月后,你无法解决为什么这个宝石一直出现!)

看起来很傻但通过限制对事物的访问可以真正帮助调试。如果你知道唯一可以添加gem的东西是GemSpawner,那么你就可以将基于宝石的潜在错误隔离到一个类。如果它可能来自任何地方,那么调试会变得更加困难,特别是当您的项目变得复杂时。

答案 1 :(得分:5)

静态数据成员在您的程序中引入了我们称之为全局状态的内容。一般来说,全球国家被认为是坏的,原因有几个:

  • 它使读取全局状态的方法的行为不可预测。这有两个影响:
    • 很难推断出一种方法,其结果取决于它所属的对象以及传递给它的参数。
    • 它可以使对该方法的正式测试的任何尝试毫无意义(尽管有一些例外)。
  • 现在可以保留假设,但不会更晚。例如,如果每个程序运行严格运行一次,则修改全局状态的过程可能没问题。但是稍后您可能会多次运行该程序。在这种情况下:
    • 按顺序,后续迭代必须注意“已使用”状态。
    • 与此同时,你将以一种难以防范的方式打开数据竞赛的大门,并且不安全。

另一方面,如果您将所需的状态作为参数传递给每个方法,那么您将创建一个安全网,自动使上述所有内容更不可能发生并威胁程序的正确性。

我怀疑在选择全球国家而不是通过国家方面有显着的绩效优势(或任何好处)。如果你最终遇到过度的全局状态验证,或者更糟糕的是,引入锁定和障碍(两种情况都非常昂贵),你将取消你可能获得的任何好处。

坚持不引入全球国家的良好做法更值得。

答案 2 :(得分:4)

public static变量本质上是一个全局变量。

在有经验的程序员中,全局变量被认为是非常差的编程风格。

Here is an article which describes some reasons why they are bad

面向对象的解决方案是将您的gems列表封装为GemManager类中的私有集合,该类通过方法公开对gem的任何访问。然后,您将创建一个GemManager实例,并将该实例传递给需要它的每个类或方法。

答案 3 :(得分:3)

这是一个比你可能意识到的要复杂得多的问题。

许多开始使用Java编写的人开始完全创建所有内容static,因为您不必传递引用 - 这会使您的代码“更简单”。

但是当您的代码变得更复杂时,您就开始遇到问题。这些问题出现了3条主线:

  1. 封装
  2. 抽象
  3. 测试
  4. Encapsulation

    这是Object不应该允许直接访问其成员的想法,应该告诉它“做事”,然后它会在内部完成,而不会暴露如何完成。

    这背后的想法是你试图避免过于紧密地将你的类彼此耦合。

    这引导我们进入下一点

    Abstraction

    在Java中,这通过abstract类和interfaces表示。

    这个想法是你的GemSpawner只是产生宝石的定义。内部如何做到这一点真的不是一个人的事,而是它自己的事。

    在Java中,你无法真正地将static方法与关键的OO继承理念进行协调。

    static方法是继承的,但是它们被遮蔽而不是被覆盖,因此您不能(轻松)修改它们的行为。

    这导致我们进入

    <强>测试

    随着程序变得越来越复杂,这个主题越来越多。

    您如何测试“Hello World”计划?好吧,你运行它,看它是否打印“Hello Wrld” - 在这种情况下有一个错误。

    一旦程序变得更复杂,你就不能简单地做到这一点。你需要分开你的程序并测试“单位”。被称为Unit Testing

    此处您的static引用确实会引发问题。您无法将程序分离为离散单元,因为所有内容都通过直接类引用绑定在一起。而且你不能模仿static方法的行为,因为它们不容易被覆盖。

    所以,总结一下。是;将static放在任何地方并且不传递引用可能会更快更容易。 但是,如果你打算写一些像游戏一样复杂的东西,你应该考虑使用Java来充分发挥它的潜力。

答案 4 :(得分:0)

使用静态变量几乎没有任何好处,但有一些缺点:

  • 您不能创建游戏的多个副本,无论是同时运行,还是作为历史副本 - 您每个JVM最大锁定一个副本
  • 您不需要存在的类之间存在依赖关系 - 请参阅coupling
  • 你可能想要从另一个班级开始游戏,但现在你不能 - 你已经把你的初级锁定到你的主要班级
  • 您已将实施“渗出”到另一个班级 - 请参阅cohesion
相关问题