如果私有帮助方法可以是静态的,那么它们应该是静态的

时间:2009-02-11 21:24:33

标签: java static methods static-methods

假设我有一个旨在实例化的类。我在类中有几个私有的“帮助器”方法,它们不需要访问任何类成员,只能在它们的参数上运行,返回结果。

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

是否有任何特殊原因要将computeOnecomputeMore指定为静态方法 - 或者是否有任何特殊原因?

将它们保留为非静态非常容易,即使它们肯定是静态的而不会造成任何问题。

21 个答案:

答案 0 :(得分:164)

我更喜欢这样的辅助方法是private static;这将使读者清楚他们不会修改对象的状态。我的IDE也会以斜体显示对静态方法的调用,所以我会知道该方法是静态的而不需要查看签名。

答案 1 :(得分:105)

可能会导致字节码略小,因为静态方法无法访问this。我不认为它在速度上有任何不同(如果确实如此,它可能太小而不能总体上有所不同。)

我会让它们变得静止,因为我一般都会这样做。但那只是我。


编辑:这个答案一直在下降,可能是因为有关字节码大小的未经证实的断言。所以我实际上会进行测试。

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

字节码(用javap -c -private TestBytecodeSize检索):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

调用静态方法需要两个字节码(byteops?):iconst_0(对于参数)和invokestatic
调用非静态方法需要三个:aload_1(对于TestBytecodeSize对象,我猜),iconst_0(对于参数)和invokespecial。 (请注意,如果这些不是私有方法,则为invokevirtual而不是invokespecial;请参阅JLS §7.7 Invoking Methods。)

现在,正如我所说的,除了invokestatic需要少一个字节码这一事实之外,我不认为这两者之间的性能会有很大差异。 invokestaticinvokespecial都应该比invokevirtual略快,因为它们都使用静态绑定而不是动态绑定,但我不知道它们是否比另一个更快。我也找不到任何好的参考资料。我能找到的最接近的是this 1997 JavaWorld article,它基本上重述了我刚才所说的内容:

  

最快的指令很可能是invokespecialinvokestatic,因为这些指令调用的方法是静态绑定的。当JVM解析这些指令的符号引用并用直接引用替换它时,该直接引用可能包含指向实际字节码的指针。

但自1997年以来,许多事情发生了变化。

总而言之......我想我仍然坚持我之前所说的话。速度不应该是选择其中一个的原因,因为它最多只是微观优化。

答案 2 :(得分:18)

我个人的偏好是宣称它们是静态的,因为它是一个明确的标志,他们是无国籍的。

答案 3 :(得分:18)

答案是......这取决于。

如果member是特定于您正在处理的对象的实例变量,那么为什么要将它作为参数传递?

例如:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}

答案 4 :(得分:11)

您可能想要声明静态辅助方法的一个原因是,您需要在“thissuper之前的类构造函数中调用它们。例如:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

这是一个人为的例子,但显然recoverInt在这种情况下不能成为实例方法。

答案 5 :(得分:10)

我无法真正想到私有静态方法的明显优势。话虽如此,使它们非静态也没有特别的优势。这主要是一个介绍问题:你可能想让它们变得静止,以明确强调它们不会改变一个对象。

对于具有不同访问权限的方法,我认为有两个主要参数:

  • 可以在不创建对象实例的情况下调用静态方法,这可能很有用
  • 静态方法不能被继承,如果你需要多态(这与私有方法无关),这可能是一个问题。

除此之外,差异非常小,我强烈怀疑传递给实例方法的额外this指针会产生重大影响。

答案 6 :(得分:7)

正确答案是:

任何不从字段中获取任何信息且不将任何信息放入字段的方法都不必是实例方法。任何不使用或改变其类或对象中的任何字段的方法也可能是静态方法。

答案 7 :(得分:5)

  

或任何特殊原因不[将它们声明为静态]?

通过将它们保留为实例方法,您可以在以后提供不同的实现。

这可能听起来很愚蠢(事实上,如果这些方法仅在50行程序中使用),但在较大的应用程序中,或者在其他人使用的库中,您可能决定选择更好的实现,但是不想破坏现有的代码。

因此,您创建一个子类并在新版本中返回它,并且由于这些方法被声明为实例方法,因此您只需让多态就可以完成它的工作。

此外,您可以从构造函数私有中受益,并出于同样的原因提供静态工厂方法。

所以,我的建议是将它们作为实例方法保留,并尽可能避免静电 充分利用语言提供的活力。

请点击此处查看相关视频: How to design a good API and why it matters

虽然它与“静态与实例”方法讨论没有直接关系,但它触及了API设计中的一些有趣点。

答案 8 :(得分:5)

关于使用静态方法的一个问题是它可以使对象在单元测试中更难使用。 Mockito无法为静态方法创建模拟,您无法创建该方法的子类实现。

答案 9 :(得分:4)

如果该方法基本上只是一个永远不会使用状态信息的子程序,则将其声明为静态。

这允许它用于其他静态方法或类初始化,即:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}

答案 10 :(得分:3)

我喜欢这样的情况是制作computeOnecomputeMore静态方法。原因是:封装。能够访问类实现的代码越少越好。

在您给出的示例中,您声明computeOnecomputeMore不应该访问类的内部,所以为什么要让类的维护者有机会干涉内部

答案 11 :(得分:3)

我想澄清一些其他海报所说的错误信息。

首先,由于这些方法是私有的,即使你声明它们是静态的,你也无法在这个类之外访问它们。其次它们是私有的,所以你甚至不能在子类中重写,所以静态或非静态没有任何区别。第三,也可以从类的构造函数中调用非静态私有方法,它不必是静态的。

现在提出问题,如果私有帮助方法应该定义为静态或非静态。我将使用Steve的答案标记私有方法静态显示此方法是无状态的,因为我在编码时也遵循此规则。

答案 12 :(得分:3)

根据我的经验,我会说这种私人方法往往是非常普遍和可重复使用的。

我认为首先要问的问题是该方法在当前类上下文之外是否有用。如果是这样的话,我会完全按照Everyone的建议去做,并将这个方法作为static提取到某个utils类,在这个类中,有人希望在实现新方法之前检查完全相同的东西。

这种一般使用私有方法是项目中代码重复的重要部分的来源,因为每个开发人员在她需要使用它的地方独立地重新发明它们。因此,集中这些方法是一种方法。

答案 13 :(得分:2)

更具体地说,您给出的示例似乎定义这些方法的目的更多是为了您在阅读它时的代码清晰度而不是为了功能(它们 定义为私有) 。在这种情况下,使用static实际上对您没有任何帮助,因为静态的目的是公开类功能。

答案 14 :(得分:2)

静态/非静态问题归结为“我真的需要使用这个类的对象吗?”

那么,你在不同方法之间传递对象吗?对象是否包含在静态方法的上下文之外有用的信息?如果你两种方式都使用它们,是否有任何理由不以两种方式定义方法?

如果你处于这种困境中,在我看来,你拥有该对象之外的代码所需的所有数据。这是你想要的吗?每次总是将数据收集到对象中会更容易吗?你可能只是对提交单一模型感到矛盾。如果你可以使用一种方法完成所有操作,那么选择静态或非静态并使用它。

答案 15 :(得分:1)

一个原因是,在其他条件相同的情况下,静态方法调用应该更快。静态方法不能是虚拟的,也不要采用隐式的引用。

答案 16 :(得分:1)

Off-Topic:我将helper方法保存在一个独立的实用程序/辅助类中,其中只包含静态方法。

在使用点使用辅助方法(读取“同一类”)的麻烦在于,有人可能只是选择在同一个地方发布他们自己的无关辅助方法

答案 17 :(得分:1)

class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

私有静态方法的优点是,如果需要重新初始化类变量,它们可以在以后重用。

答案 18 :(得分:1)

  1. 如果没有静态修饰符,您无法确定该方法是无状态的,无需额外分析,这可以在您(重新)编写方法时轻松完成。

  2. 然后,“静态”修饰符可能会为您提供有关重构的想法,以及其他人可能认为无用的其他内容。 例如。将方法移动到某个Utility类或将其转换为成员方法..

答案 19 :(得分:0)

我会将它们声明为静态,将它们标记为无状态。

Java没有更好的机制来处理未导出的小操作,因此我认为私有静态是可以接受的。

答案 20 :(得分:0)

正如很多人所说,将其作为 静态 ! 这是我遵循的拇指规则:如果您认为该方法只是一个数学函数即它是无状态的,则不涉及任何实例变量(=&gt;没有蓝色变量[在eclipse中]对于'n'个调用(具有相同的参数,当然),该方法的结果将是相同的,然后将该方法标记为STATIC。

如果您认为此方法对其他类有用,则将其移至Util类,否则将该方法作为private放在同一类中。 (最小化可访问性)