假设我有一个被设计为实例化的类。我在类中有几个私有的“helper”方法,它们不需要访问任何类成员,只对它们的参数进行操作,并返回一个结果。

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) {... } 
} 

是否有特别的理由指定computeOne和computeMore作为静态方法-或者有特别的理由不这样做?

当然,让它们保持非静态状态是最简单的,尽管它们当然可以是静态的,而不会引起任何问题。


当前回答

这可能会导致字节码略小,因为静态方法无法访问它。我不认为它在速度上有任何不同(如果有的话,它可能太小了,不会对整体产生影响)。

我会让它们是静态的,因为如果可能的话,我通常会这样做。但那只是我。


编辑:这个答案一直被否决,可能是因为关于字节码大小的未经证实的断言。我来做个测试。

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。(请注意,如果这些不是私有方法,它将是invokvirtual而不是invokspecial;参见JLS§7.7调用方法。)

现在,正如我所说,我不期望这两者之间有任何很大的性能差异,除了invokestatic需要的字节码少了一个。invokestatic和invokspecial应该都比invokvirtual稍微快一点,因为它们都使用静态绑定而不是动态绑定,但我不知道是否其中一个比另一个更快。我也找不到任何好的推荐信。我能找到的最接近的是1997年JavaWorld的文章,它基本上重申了我刚才所说的:

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

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

总之…我想我还是坚持我之前说过的话。速度不应该是选择一个而不是另一个的原因,因为这充其量只是一个微观优化。

其他回答

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

对于不能导出的小操作,Java没有更好的机制,所以我认为私有静态是可以接受的。

我更喜欢这样的帮助方法是私有静态的;这将使读者清楚,他们不会修改对象的状态。我的IDE还将以斜体显示对静态方法的调用,因此我无需查看签名就知道该方法是静态的。

跑题:我会把辅助方法放在一个独立的实用程序/辅助类中,其中只有静态方法。

在使用点(阅读为“相同的类”)拥有帮助方法的麻烦在于,某些人可能会选择将他们自己不相关的帮助方法发布在相同的位置

这可能会导致字节码略小,因为静态方法无法访问它。我不认为它在速度上有任何不同(如果有的话,它可能太小了,不会对整体产生影响)。

我会让它们是静态的,因为如果可能的话,我通常会这样做。但那只是我。


编辑:这个答案一直被否决,可能是因为关于字节码大小的未经证实的断言。我来做个测试。

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。(请注意,如果这些不是私有方法,它将是invokvirtual而不是invokspecial;参见JLS§7.7调用方法。)

现在,正如我所说,我不期望这两者之间有任何很大的性能差异,除了invokestatic需要的字节码少了一个。invokestatic和invokspecial应该都比invokvirtual稍微快一点,因为它们都使用静态绑定而不是动态绑定,但我不知道是否其中一个比另一个更快。我也找不到任何好的推荐信。我能找到的最接近的是1997年JavaWorld的文章,它基本上重申了我刚才所说的:

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

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

总之…我想我还是坚持我之前说过的话。速度不应该是选择一个而不是另一个的原因,因为这充其量只是一个微观优化。

使用静态方法的一个问题是,它会使对象在单元测试中更难使用。Mockito不能为静态方法创建模拟,也不能创建该方法的子类实现。