ava*_*lys 197 java methods static 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) {... }
}
Run Code Online (Sandbox Code Playgroud)
有没有特别的理由指定computeOne和computeMore作为静态方法 - 或任何特殊原因不?
将它们保持为非静态是最容易的,即使它们肯定是静态的而不会引起任何问题.
Esk*_*ola 169
我更喜欢这样的辅助方法private static; 这将使读者清楚他们不会修改对象的状态.我的IDE也会以斜体显示对静态方法的调用,所以我会知道该方法是静态的而不需要查看签名.
Mic*_*ers 108
它可能会导致字节码略小,因为静态方法无法访问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);
}
}
Run Code Online (Sandbox Code Playgroud)
字节码(检索到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
}
Run Code Online (Sandbox Code Playgroud)
调用静态方法需要两个字节码(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年以来,许多事情发生了变化
总而言之......我想我仍然坚持我之前所说的话.速度不应该是选择其中之一的原因,因为它最多只是微观优化.
Pow*_*ord 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 */ }
}
Run Code Online (Sandbox Code Playgroud)
oxb*_*kes 11
您可能想要声明静态辅助方法的一个原因是,您需要在"之前" 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));
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个人为的例子,但recoverInt在这种情况下显然不能成为实例方法.
Axe*_*ler 10
我无法真正想到私有静态方法的明显优势.话虽如此,使它们非静态也没有特别的优势.这主要是一个介绍问题:你可能想让它们变得静态,以明确强调它们不会改变一个对象.
对于具有不同访问权限的方法,我认为有两个主要参数:
除此之外,差异非常小,我强烈怀疑传递给实例方法的额外this指针会产生显着的差异.
如果该方法基本上只是一个永远不会使用状态信息的子程序,请将其声明为静态。
这允许它在其他静态方法或类初始化中使用,即:
public class Example {
//...
//Only possible if computeOne is static
public final static double COMPUTED_ONE = computeOne(new Something("1"));
//...
}
Run Code Online (Sandbox Code Playgroud)
或任何特殊原因不[声明它们是静态的]?
是.
通过将它们保留为实例方法,您可以在以后提供不同的实现.
它可能听起来很愚蠢(实际上如果这些方法仅在50行程序中由你使用),但在较大的应用程序中,或者在其他人使用的库中,你可能决定选择更好的实现,但不要想破坏现有的代码.
因此,您创建一个子类并在新版本中返回它,并且由于这些方法被声明为实例方法,因此您只需让多态就可以完成它的工作.
此外,您可以从构造函数私有中受益,并出于同样的原因提供静态工厂方法.
所以,我的建议是将它们作为实例方法,并尽可能避免静态.
充分利用语言提供的活力.
请看这里有一个相关的视频:如何设计一个好的API及其重要性
虽然它与"静态与实例"方法讨论没有直接关系,但它触及了API设计中的一些有趣点.
关于使用静态方法的一个问题是它可以使对象在单元测试中更难以使用.Mockito无法为静态方法创建模拟,也无法创建方法的子类实现.