假设我有一个旨在实例化的类.我在类中有几个私有的"帮助器"方法,它们不需要访问任何类成员,并且只对它们的参数进行操作,返回结果.
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
作为静态方法 - 或任何特殊原因不?
将它们保持为非静态是最容易的,即使它们肯定是静态的而不会引起任何问题.
我更喜欢这样的辅助方法private static
; 这将使读者清楚他们不会修改对象的状态.我的IDE也会以斜体显示对静态方法的调用,所以我会知道该方法是静态的而不需要查看签名.
它可能会导致字节码略小,因为静态方法无法访问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."":()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 " ":()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调用方法.)
现在,正如我所说,我不认为这两者之间的性能会有很大差异,除了invokestatic
需要少一个字节码的事实.invokestatic
并且invokespecial
两者都应该稍快invokevirtual
,因为它们都使用静态绑定而不是动态,但我不知道是否比另一个更快.我也找不到任何好的参考资料.我能找到的最接近的是1997年的JavaWorld文章,它基本上重述了我刚才所说的内容:
最快的指令很可能是
invokespecial
和invokestatic
,因为这些指令调用的方法是静态绑定的.当JVM解析这些指令的符号引用并用直接引用替换它时,该直接引用可能包含指向实际字节码的指针.
但自1997年以来,许多事情发生了变化
总而言之......我想我仍然坚持我之前所说的话.速度不应该是选择其中之一的原因,因为它最多只是微观优化.
我个人的偏好是宣布它们是静态的,因为它是一个明确的标志,他们是无国籍的.
答案是......这取决于.
如果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 */ } }
您可能想要声明静态辅助方法的一个原因是,您需要在"之前" this
或之前的类构造函数中调用它们super
.例如:
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
在这种情况下显然不能成为实例方法.
我无法真正想到私有静态方法的明显优势.话虽如此,使它们非静态也没有特别的优势.这主要是一个介绍问题:你可能想让它们变得静态,以明确强调它们不会改变一个对象.
对于具有不同访问权限的方法,我认为有两个主要参数:
可以在不创建对象实例的情况下调用静态方法,这可能很有用
静态方法不能被继承,如果你需要多态(这与私有方法无关),这可能是一个问题.
除此之外,差异非常小,我强烈怀疑传递给实例方法的额外this指针会产生显着的差异.
正确的答案是:
任何不从字段中获取任何信息且不将任何信息放入字段的方法都不一定是实例方法.任何不使用或改变其类或对象中的任何字段的方法也可能是静态方法.
或任何特殊原因不[声明它们是静态的]?
是.
通过将它们保留为实例方法,您可以在以后提供不同的实现.
它可能听起来很愚蠢(实际上如果这些方法仅在50行程序中由你使用),但在较大的应用程序中,或者在其他人使用的库中,你可能决定选择更好的实现,但不要想破坏现有的代码.
因此,您创建一个子类并在新版本中返回它,并且由于这些方法被声明为实例方法,因此您只需让多态就可以完成它的工作.
此外,您可以从构造函数私有中受益,并出于同样的原因提供静态工厂方法.
所以,我的建议是将它们作为实例方法,并尽可能避免静态.
充分利用语言提供的活力.
请看这里有一个相关的视频:如何设计一个好的API及其重要性
虽然它与"静态与实例"方法讨论没有直接关系,但它触及了API设计中的一些有趣点.
关于使用静态方法的一个问题是它可以使对象在单元测试中更难以使用.Mockito无法为静态方法创建模拟,也无法创建方法的子类实现.