Java 如果私有助手方法可以是静态的,那么它们应该是静态的吗
假设我设计了一个要实例化的类。我在类中有几个私有的“helper”方法,它们不需要访问任何类成员,只对它们的参数进行操作,并返回结果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);
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
}
}