在Java中使用final关键字可以提高性能吗?

在Java中我们看到很多地方可以使用final关键字,但是它的使用是不常见的。

例如:

 String str = "abc"; System.out.println(str); 

在上面的例子中, str可以是final但是这通常是中止的。

当一个方法永远不会被覆盖时,我们可以使用final关键字。 类似的情况下,不会被inheritance的类。

在任何或所有这些情况下使用final关键字是否真正提高性能? 如果是这样,那么怎么样? 请解释。 如果正确使用final对于性能来说真的很重要,那么Java程序员应该开发哪些习惯来充分利用关键字呢?

通常不会。 对于虚拟方法,HotSpot会跟踪方法是否被实际覆盖,并且能够执行优化,比如假设一个方法没有被覆盖,直到它加载一个覆盖方法的类,在这个点它可以撤销(或部分撤消)这些优化。

(当然,这是假设你正在使用HotSpot–但它是迄今为止最常见的JVM,所以…)

在我看来,你应该使用清晰的devise和可读性而不是出于性能的原因。 如果出于性能原因想要改变任何东西,那么在将最清晰的代码弯曲变形之前,您应该进行适当的测量 – 这样您就可以决定是否有任何额外的性能值得这种较差的可读性/devise。 (根据我的经验,这几乎是不值得的; YMMV。)

编辑:作为最后的领域已经提到,值得提出,无论如何,他们往往是一个好主意,在清晰的devise方面。 它们还会根据跨线程可见性更改保证的行为:在构造函数完成之后,任何最终字段都将保证在其他线程中立即可见。 虽然作为Josh Bloch的“inheritance或禁止devise”的经验的支持者,我可能最常用的是final

简短的回答:不要担心!

很长的回答:

在谈到最终的局部variables时,请记住,使用关键字final可以帮助编译器静态优化代码,最终可能会得到更快的代码。 例如,以下示例中的最后一个stringa + b是静态连接的(在编译时)。

 public class FinalTest { public static final int N_ITERATIONS = 1000000; public static String testFinal() { final String a = "a"; final String b = "b"; return a + b; } public static String testNonFinal() { String a = "a"; String b = "b"; return a + b; } public static void main(String[] args) { long tStart, tElapsed; tStart = System.currentTimeMillis(); for (int i = 0; i < N_ITERATIONS; i++) testFinal(); tElapsed = System.currentTimeMillis() - tStart; System.out.println("Method with finals took " + tElapsed + " ms"); tStart = System.currentTimeMillis(); for (int i = 0; i < N_ITERATIONS; i++) testNonFinal(); tElapsed = System.currentTimeMillis() - tStart; System.out.println("Method without finals took " + tElapsed + " ms"); } } 

结果?

 Method with finals took 5 ms Method without finals took 273 ms 

在Java Hotspot VM 1.7.0_45-b18上进行testing。

那么实际的性能改善有多大? 我不敢说。 在大多数情况下,这个综合testing可能是微乎其微的(大约270纳秒,因为string级联是完全避免的) – 但是在高度优化的实用程序代码中,这可能是一个因素。 在任何情况下,对原始问题的答案都是肯定的,这可能会提高性能,但最多只能略微提高

撇开编译时间的好处,我找不到任何证据表明关键字final的使用对性能有任何可衡量的影响。

是的,它可以。 这是一个最终可以提高性能的例子:

条件编译是一种将代码行根据特定条件编译到类文件中的技术。 这可以用来删除生产版本中的大量debugging代码。

考虑以下几点:

 public class ConditionalCompile { private final static boolean doSomething= false; if (doSomething) { // do first part. } if (doSomething) { // do second part. } if (doSomething) { // do third part. } if (doSomething) { // do finalization part. } } 

通过将doSomething属性转换为最终属性,您告诉编译器,只要它看到doSomething,就应该按照编译时replace规则将其replace为false。 编译器的第一遍将代码更改为如下所示:

 public class ConditionalCompile { private final static boolean doSomething= false; if (false){ // do first part. } if (false){ // do second part. } if (false){ // do third part. } if (false){ // do finalization part. } } 

一旦完成,编译器会再看看它,并看到代码中有无法访问的语句。 由于您正在使用高质量的编译器,因此不会像所有那些无法访问的字节码。 所以它把它们删除了,最后你得到了这个结果:

 public class ConditionalCompile { private final static boolean doSomething= false; public static void someMethodBetter( ) { // do first part. // do second part. // do third part. // do finalization part. } } 

从而减less任何过多的代码或任何不必要的条件检查。

据IBM介绍,它不适用于类或方法。

http://www.ibm.com/developerworks/java/library/j-jtp04223.html

你真的在问两个(至less)不同的情况:

  1. final是局部variables
  2. final的方法/类

Jon Skeet已经回答了2)。 关于1):

我不认为这有什么区别。 对于局部variables,编译器可以推断variables是否是最终的(仅仅通过检查是否被赋值多次)。 所以,如果编译器想优化只分配一次的variables,无论variables是否被实际声明为final ,都可以这样做。

final 可能会对保护/公共职业领域有所作为; 那么编译器就很难知道这个字段是否被设置了多次,因为它可能发生在一个不同的类中(甚至可能没有被加载)。 但即使如此,JVM也可以使用Jon所描述的技术(乐观地优化,如果一个类被加载,会改变字段)。

总之,我看不出有什么理由来帮助业绩。 所以这种微观优化不太可能有帮助。 你可以尝试对其进行基准testing,但是我怀疑它会有所作为。

编辑:

事实上,根据TimoWestkämper的回答, final 可以在某些情况下提高class级领域的performance。 我纠正了。

注意:不是java专家

如果我正确地记得我的java,那么使用final关键字提高性能的方法将会很less。 我一直都知道它存在“好代码” – devise和可读性。

实际上,在testing一些与OpenGL相关的代码时,我发现在私有字段上使用最终修饰符会降低性能。 这是我testing的课程的开始:

 public class ShaderInput { private /* final */ float[] input; private /* final */ int[] strides; public ShaderInput() { this.input = new float[10]; this.strides = new int[] { 0, 4, 8 }; } public ShaderInput x(int stride, float val) { input[strides[stride] + 0] = val; return this; } // more stuff ... 

这是我用来testing各种替代品的性能的方法,其中ShaderInput类:

 public static void test4() { int arraySize = 10; float[] fb = new float[arraySize]; for (int i = 0; i < arraySize; i++) { fb[i] = random.nextFloat(); } int times = 1000000000; for (int i = 0; i < 10; ++i) { floatVectorTest(times, fb); arrayCopyTest(times, fb); shaderInputTest(times, fb); directFloatArrayTest(times, fb); System.out.println(); System.gc(); } } 

在第三次迭代之后,随着VM的升温,我始终得到这些数字, 没有最后的关键词:

 Simple array copy took : 02.64 System.arrayCopy took : 03.20 ShaderInput took : 00.77 Unsafe float array took : 05.47 

最后一个关键字:

 Simple array copy took : 02.66 System.arrayCopy took : 03.20 ShaderInput took : 02.59 Unsafe float array took : 06.24 

注意ShaderInputtesting的数字。

无论我是公开的还是私人的,我都没有关系。

顺便提一下,还有一些令人困惑的事情。 即使使用final关键字,ShaderInput类也会优于所有其他变体。 这是显着的B / C它基本上是一个包装浮法数组的类,而其他testing直接操纵数组。 必须弄清楚这一个。 可能与ShaderInputstream畅的界面有关。

另外System.arrayCopy显然对于小数组来说显然比在for循环中将元素从一个数组复制到另一个数组慢。 而使用sun.misc.Unsafe(以及直接的java.nio.FloatBuffer,这里没有显示)performance的非常糟糕。

我不是一个专家,但我想你应该添加final关键字的类或方法,如果它不会被覆盖,并保持单独的variables。 如果有什么办法来优化这些东西的话,编译器会为你做。

肯定是的,如果variables转换为常量,

因为我们知道java编译器将这样的最终variables转换为常量可能的值

作为常量java编译器的概念,在编译时直接用它的引用replace它的值

在java中,如果variables是没有任何运行时进程给出的string或基本types,则它们将变为常量

否则它只是final(不可变)variables,

&constatnt的使用总是比引用更快。

所以如果可能的话,使用任何编程语言的常量来获得更好的性能

final关键字可以在Java中以五种方式使用。

  1. 一个class是最后的
  2. 参考variables是最终的
  3. 局部variables是最终的
  4. 一个方法是最终的

一个阶级是最终的:一个阶级是最后的意思,我们不能延续或inheritance意味着inheritance是不可能的。

同样 – 一个对象是final的:有时候我们并没有修改对象的内部状态,所以在这种情况下我们可以指定对象是最终的object.object,最后的意思是不可变也是final。

一旦引用variables是最终的,它不能被重新分配给其他对象。 但只要字段不是最终的,就可以改变对象的内容