静态方法与非静态方法在实用类中的性能比较

20
我有一个实用程序类,其中包含没有实例变量的非静态方法。因此,我考虑将所有方法转换为static方法。我怀疑这不会对内存或性能产生任何影响,但我想确认一下。
将这样的方法更改为static是否会对程序产生性能影响?
7个回答

25

还有一件事情需要补充。

使用static方法会稍微降低开销,因为您可以保证编译时绑定。静态方法调用将创建字节码指令invokestatic

在典型的情况下,实例方法是在运行时绑定的,并且将创建字节码指令invokevirtual,其开销比invokestatic更高。

然而,这只有在可能进行数百万次迭代的情况下才变得相关,我建议不要让这驱动您的类设计。从设计角度出发做出合理的选择。根据您的描述,static方法可能是正确的选择。实际上,创建一个实用程序类是相对标准的做法:

public class MyUtilities {
   private MyUtilities() { } // don't let anyone construct it.
   public static String foo(String s) { ... }
}

1
这个故事有点复杂,因为invokevirtual不是一个实际的机器指令。在最坏的情况下,它将被实现为单态调用站点,但如果类是final(或通过私有构造函数有效地成为final),它将是一个硬连线调用。即使没有这样做,运行时也可能注意到没有子类并且硬连线调用。 - Marko Topolnik
是的,我明白这并不保证它没有硬连线,但我试图让解释简单一些,因为大多数人不理解jvm的内部机制。 - Matt
1
我必须说你的措辞有误导性。这里没有所谓的“典型场景”:实例方法调用总是编译为invokevirtual;同样,静态方法也是如此,使用invokestatic - Marko Topolnik

19

编辑:针对性能方面的问题:不必无意义地创建某些实例会更便宜,但差异很可能是完全不相关的。集中精力设计清晰的架构很可能随着时间的推移变得更加重要。

实用方法通常是静态的,如果类中的所有方法都是静态的,则值得将该类设置为final并包含一个私有构造函数以防止实例化。基本上,对于不表示任何真实“东西”的实用程序类,构造实例没有逻辑意义-所以要防止它。

另一方面,这确实降低了灵活性:如果这些实用程序方法中的任何一个包含您可能希望通过多态方式进行变化的功能(例如,用于测试目的),则考虑将它们保留为实例方法-并尝试提取一些有意义的类名来表示涉及的“事物”。 (例如,FooConverter有意义实例化-FooUtil不是)


4
对于第二段加一。如果使用静态方法,则无法交换实现方式。 - Petar Minchev
@MarkoTopolnik:已在答案开头添加了一个部分。 - Jon Skeet

4
有两个要求必须满足才能将方法转换为static:
  1. 不访问实例变量(在您的情况下已经满足);
  2. 永远不需要被覆盖(对此,您可能需要仔细考虑)。
然而,当这些要求得到满足时,实际上建议将该方法设置为静态方法,因为它限制了该方法所运行的上下文。
最后,注意这里没有性能问题可言,任何理论差异实际上都有利于静态方法,因为它们不涉及动态方法解析。但是,在任何相关的JVM实现中,实例方法调用都是非常快的。
至于内存方面,情况也是如此:理论上,静态方法有优势,但如果与单例实用程序类进行比较,则实际上没有实际差异。

3
如果实用类没有被子类化,将不访问实例变量的转换方法转换为 static 是一个好主意。您应该仔细检查代码并将调用转换为静态语法,即:
int res = utilityInstance.someMethod(arg1, arg2);

应该转换为

int res = UtilityClass.someMethod(arg1, arg2);

为了更好的表达清晰度。

不会有明显的性能影响:尽管理论上静态调用略微廉价,但在大多数情况下,这种差异太小以至于不值得考虑。


1

对于没有状态的实用类(例如java.lang.Math),通常具有公共静态方法。这样,您无需创建类的实例即可使用它。


0

当你需要经常使用特定功能时,静态方法是一个好主意。


-2

区别在于您需要一个实例才能使用它们,因此用户必须创建一个实例,该实例将是一个


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接