要回答标题上的问题,一般情况下,Java 方法应该不默认是静态的。 Java 是一种面向对象的语言。
但是,你所说的有点不同。您专门谈到了辅助方法。
对于仅将值作为参数并返回值而不访问状态的辅助方法,它们应该是静态的。私有的和静态的。让我强调一下:
不访问状态的辅助方法应该是静态的。
1。主要优点:代码更具表现力。
将这些方法设为静态至少有一个主要优势:您可以在代码中完全明确该方法不需要知道任何实例状态。。 p>
代码不言自明。对于将阅读您的代码的其他人来说,事情变得更加明显,甚至在未来的某个时候对您来说也是如此。
2。另一个优点:代码可以更简单地推理。
如果您确定该方法不依赖于外部或全局状态,那么它就是一个纯函数,即数学意义上的函数:对于相同的输入,你可以肯定得到始终相同的输出。
3。优化优势
如果该方法是静态的并且是一个纯函数,那么在某些情况下它可能是memoized 以获得一些性能提升(改变使用更多内存)。
4。字节码级差异
在字节码层面,如果将辅助方法声明为实例方法或静态方法,则会得到两个完全不同的东西。
为了让本节更容易理解,我们举个例子:
public class App {
public static void main(String[] args) {
WithoutStaticMethods without = new WithoutStaticMethods();
without.setValue(1);
without.calculate();
WithStaticMethods with = new WithStaticMethods();
with.setValue(1);
with.calculate();
}
}
class WithoutStaticMethods {
private int value;
private int helper(int a, int b) {
return a * b + 1;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int calculate() {
return helper(value, 2 * value);
}
}
class WithStaticMethods {
private int value;
private static int helper(int a, int b) {
return a * b + 1;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int calculate() {
return helper(value, 2 * value);
}
}
我们感兴趣的行是在 WithoutStaticMethods 和 WithStaticMethods 类上对 helper(...) 的调用。
没有静态方法
在第一种情况下,没有静态方法,当您调用辅助方法时,JVM 需要推送对实例的引用以将其传递给invokespecial。看一下calculate()方法的代码:
0 aload_0
1 aload_0
2 getfield #2 <app/WithoutStaticMethods.value>
5 iconst_2
6 aload_0
7 getfield #2 <app/WithoutStaticMethods.value>
10 imul
11 invokespecial #3 <app/WithoutStaticMethods.helper>
14 ireturn
0(或1)处的指令aload_0将加载对堆栈上实例的引用,稍后将被invokespecial使用。该指令将该值作为helper(...) 函数的第一个参数,并且从未使用过,正如我们在此处看到的:
0 iload_1
1 iload_2
2 imul
3 iconst_1
4 iadd
5 ireturn
看到没有iload_0?它被不必要地加载了。
使用静态方法
现在,如果您声明辅助方法是静态的,那么 calculate() 方法将如下所示:
0 aload_0
1 getfield #2 <app/WithStaticMethods.value>
4 iconst_2
5 aload_0
6 getfield #2 <app/WithStaticMethods.value>
9 imul
10 invokestatic #3 <app/WithStaticMethods.helper>
13 ireturn
区别在于:
- 少了一条
aload_0指令
- 现在使用
invokestatic 调用辅助方法
嗯,帮助函数的代码也有点不同:没有this作为第一个参数,所以参数实际上在位置0和1,我们可以在这里看到:
0 iload_0
1 iload_1
2 imul
3 iconst_1
4 iadd
5 ireturn
结论
从代码设计的角度来看,将辅助方法声明为静态更有意义:代码自己说话,它包含更多有用的信息。它声明它不需要实例状态来工作。
在字节码级别,发生了什么更加清楚,并且没有无用的代码(虽然我相信 JIT 无法优化它,但不会产生显着的性能成本)。