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

Java 如果私有助手方法可以是静态的,那么它们应该是静态的吗,java,static,methods,static-methods,Java,Static,Methods,Static Methods,假设我设计了一个要实例化的类。我在类中有几个私有的“helper”方法,它们不需要访问任何类成员,只对它们的参数进行操作,并返回结果 public class Example { private Something member; public double compute() { double total = 0; total += computeOne(member); total += computeMore(member);

假设我设计了一个要实例化的类。我在类中有几个私有的“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
检索):

从“TestBytecodeSize.java”编译而来
类TestBytecodeSize扩展了java.lang.Object{
TestBytecodeSize();
代码:
0:aload_0
1:invokespecial#1;//方法java/lang/Object。“:()V
4:返回
私人无效剂量测定(int);
代码:
0:返回
专用静态空隙剂量仪静态(int);
代码:
0:返回
公共静态void main(java.lang.String[]);
代码:
0:iconst_0
1:invokestatic#2;//方法doSomethingStatic:(I)V
4:iconst_0
5:invokestatic#2;//方法doSomethingStatic:(I)V
8:new#3;//类TestBytecodeSize
11:dup
12:invokespecial#4;//方法“”:()V
15:astore_1
16:aload_1
17:iconst_0
18:invokespecial#5;//方法doSomething:(I)V
21:aload_1
22:iconst_0
23:invokespecial#5;//方法doSomething:(I)V
26:返回
}
调用静态方法需要两个字节码(字节码?):
iconst_0
(用于参数)和
invokestatic

调用非静态方法需要三个步骤:
aload_1
(我想对于
TestBytecodeSize
对象)、
iconst_0
(对于参数)和
invokespecial
。(请注意,如果这些不是私有方法,那么它将是
invokevirtual
,而不是
invokespecial
;请参阅。)

现在,正如我所说的,我不希望这两者在性能上有什么大的区别,除了
invokestatic
需要更少的字节码这一事实
invokestatic
invokespecial
都应该比
invokevirtual
稍快一些,因为它们都使用静态绑定而不是动态绑定,但我不知道其中一个是否比另一个快。我也找不到任何好的推荐人。我能找到的最接近的是,这基本上重申了我刚才所说的:

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

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


总之。。。我想我还是坚持我以前说过的话。速度不应该是选择一种方法而不是另一种方法的理由,因为它最多只能是一种微观优化。

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

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

那么,您是在不同的方法之间传递对象吗?对象是否包含在静态方法上下文之外有用的信息?如果同时使用方法,有什么理由不同时定义方法吗


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

我真的想不出私有静态方法的明显优势。也就是说,使它们非静态也没有什么特殊的优势。这主要是一个表示问题:您可能希望使它们保持静态,以清楚地强调它们没有改变对象这一事实

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

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

除此之外,差异非常小,我强烈怀疑传递给实例方法的额外this指针是否会产生显著的差异。

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

如果该方法基本上只是一个无法预见的子例程
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

}
public class Example {
   //...

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

   //...
}
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 */ } 
}
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));
    }
}
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}