假设我有一个被设计为实例化的类。我在类中有几个私有的“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年以来,很多事情都发生了变化。
总之…我想我还是坚持我之前说过的话。速度不应该是选择一个而不是另一个的原因,因为这充其量只是一个微观优化。
或者有什么特别的理由不(将它们声明为静态的)?
Yes.
通过将它们保留为实例方法,您可以在以后提供不同的实现。
这听起来可能很愚蠢(实际上如果这些方法只由您在50行程序中使用,则会很愚蠢),但在较大的应用程序中,或在其他人使用的库中,您可能会决定选择更好的实现,但又不想破坏现有代码。
因此,在新版本中创建一个子类并返回该子类,由于方法被声明为实例方法,因此只需让多态性完成它的工作。
此外,出于同样的原因,将构造函数设置为私有并提供静态工厂方法也会带来好处。
因此,我的建议是保留它们作为实例方法,并尽可能避免静态方法。
充分利用语言提供的动态特性。
请参见这里的相关视频:如何设计一个好的API以及为什么它很重要
虽然它与“静态vs实例”方法的讨论没有直接关系,但它涉及到API设计中一些有趣的点。