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

本文翻译自:Does use of final keyword in Java improve the performance?

In Java we see lots of places where the final keyword can be used but its use is uncommon. 在Java中,我们看到很多可以使用final关键字的地方,但它的使用并不常见。

For example: 例如:

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

In the above case, str can be final but this is commonly left off. 在上面的例子中, str可以是final但通常不会这样。

When a method is never going to be overridden we can use final keyword. 当一个方法永远不会被覆盖时,我们可以使用final关键字。 Similarly in case of a class which is not going to be inherited. 类似地,如果一个类不会被继承。

Does the use of final keyword in any or all of these cases really improve performance? 在任何或所有这些案例中使用final关键字是否真的能提高性能? If so, then how? 如果是这样,那怎么样? Please explain. 请解释。 If the proper use of final really matters for performance, what habits should a Java programmer develop to make best use of the keyword? 如果正确使用final对性能有用,那么Java程序员应该养成哪些习惯来充分利用关键字?


#1楼

参考:https://stackoom.com/question/HxGu/在Java中使用final关键字可以提高性能吗


#2楼

final keyword can be used in five ways in Java. final关键字可以在Java中以五种方式使用。

  1. A class is final 课程是最终的
  2. A reference variable is final 参考变量是最终的
  3. A local variable is final 局部变量是最终的
  4. A method is final 方法是最终的

A class is final: a class is final means we cannot be extended or inheritance means inheritance is not possible. 一个类是final:一个类是final意味着我们不能被扩展或继承意味着继承是不可能的。

Similarly - A object is final: some time we does not modified the internal state of object so in such case we can specify the object is final object.object final means not variable also final. 类似地 - 一个对象是最终的:有时我们没有修改对象的内部状态,所以在这种情况下我们可以指定对象是最终对象。对象最终意味着不是变量也是最终的。

Once reference variable is made final, it cannot be reassigned to other object. 一旦引用变量为final,就不能将其重新赋值给其他对象。 But can change the contents of the object as long as its fields are not final 但只要字段不是最终字段,就可以更改对象的内容


#3楼

YES it can. 是的,它可以。 Here is an instance where final can boost performance: 这是final可以提升性能的实例:

Conditional compilation is a technique in which lines of code are not compiled into the class file based on a particular condition. 条件编译是一种技术,其中代码行不会根据特定条件编译到类文件中。 This can be used to remove tons of debugging code in a production build. 这可用于删除生产版本中的大量调试代码。

consider the following: 考虑以下:

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. 
    }
}

By converting the doSomething attribute into a final attribute, you have told the compiler that whenever it sees doSomething, it should replace it with false as per the compile-time substitution rules. 通过将doSomething属性转换为final属性,您告诉编译器每当它看到doSomething时,它应该根据编译时替换规则将其替换为false。 The first pass of the compiler changes the code to something like this: 编译器的第一遍改变的代码这样

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. 

    }
}

Once this is done, the compiler takes another look at it and sees that there are unreachable statements in the code. 完成此操作后,编译器将再次查看它,并发现代码中存在无法访问的语句。 Since you are working with a top-quality compiler, it doesn't like all those unreachable byte codes. 由于您使用的是高质量的编译器,因此它不喜欢所有那些无法访问的字节代码。 So it removes them, and you end up with this: 所以它删除它们,你最终得到这个:

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. 

  }
}

thus reducing any excessive codes, or any unnecessary conditional checking. 从而减少任何过多的代码,或任何不必要的条件检查。

Edit: As an example, let's take the following code: 编辑:举个例子,我们来看下面的代码:

public class Test {
    public static final void main(String[] args) {
        boolean x = false;
        if (x) {
            System.out.println("x");
        }
        final boolean y = false;
        if (y) {
            System.out.println("y");
        }
        if (false) {
            System.out.println("z");
        }
    }
}

When compiling this code with Java 8 and decompiling with javap -c Test.class we get: 使用Java 8编译此代码并使用javap -c Test.class反编译时,我们得到:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #22                 // String x
      11: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iconst_0
      15: istore_2
      16: return
}

We can note that compiled code includes only the non-final variable x . 我们可以注意到编译的代码只包含非最终变量x This prooves that final variables have impact on performances, at least for this simple case. 这证明了最终变量对性能的影响,至少对于这个简单的情况。


#4楼

Short answer: don't worry about it! 简答:不要担心!

Long answer: 答案很长:

When talking about final local variables keep in mind that using the keyword final will help the compiler optimize the code statically , which may in the end result in faster code. 在谈论最终局部变量时请记住,使用关键字final将有助于编译器静态优化代码,这最终可能导致更快的代码。 For example, the final Strings a + b in the example below are concatenated statically (at compile time). 例如,下面示例中的最终字符串a + 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");

    }

}

The result? 结果?

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

Tested on Java Hotspot VM 1.7.0_45-b18. 在Java Hotspot VM 1.7.0_45-b18上测试。

So how much is the actual performance improvement? 那么实际的性能提升了多少? I don't dare say. 我不敢说。 In most cases probably marginal (~270 nanoseconds in this synthetic test because the string concatenation is avoided altogether - a rare case), but in highly optimized utility code it might be a factor. 在大多数情况下可能是边缘的(在这个综合测试中大约270纳秒,因为完全避免了字符串连接 - 这是一种罕见的情况),但在高度优化的实用程序代码中,它可能是一个因素。 In any case the answer to the original question is yes, it might improve performance, but marginally at best . 在任何情况下,原始问题的答案都是肯定的,它可能会提高性能,但最多只是略微提高

Compile-time benefits aside, I could not find any evidence that the use of the keyword final has any measurable effect on performance. 除了编译时的好处,我找不到任何证据表明关键字final的使用对性能有任何可衡量的影响。


#5楼

Actually, while testing some OpenGL-related code, I found that using the final modifier on a private field can degrade performance. 实际上,在测试一些与OpenGL相关的代码时,我发现在私有字段上使用final修饰符会降低性能。 Here is the start of the class I tested: 这是我测试的课程的开始:

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 ...

And this is the method I used to test the performance of various alternatives, amongst which the ShaderInput class: 这是我用来测试各种替代方案的性能的方法,其中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();
    }
}

After the 3rd iteration, with the VM warmed up, I consistently got these figures without the final key word: 在第3次迭代之后,随着VM的预热,我一直得到这些数字而没有最后的关键词:

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

With the final keyword: 使用 final关键字:

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

Note the figures for the ShaderInput test. 请注意ShaderInput测试的数字。

It didn't matter whether I made the fields public or private. 我是将公共场所还是私人场地都无关紧要。

Incidentally, there are a few more baffling things. 顺便说一句,还有一些令人困惑的事情。 The ShaderInput class outperforms all other variants, even with the final keyword. ShaderInput类优于所有其他变体,即使使用final关键字也是如此。 This is remarkable b/c it basically is a class wrapping a float array, while the other tests directly manipulate the array. 这是非常了不起的b / c它基本上是一个包装浮点数组的类,而其他测试直接操作数组。 Have to figure this one out. 必须弄清楚这个。 May have something to do with ShaderInput's fluent interface. 可能与ShaderInput的流畅界面有关。

Also System.arrayCopy actually apparently is somewhat slower for small arrays than simply copying elements from one array to the other in a for loop. 对于小型数组而言,System.arrayCopy实际上显然比在for循环中将元素从一个数组复制到另一个数组要慢一些。 And using sun.misc.Unsafe (as well as direct java.nio.FloatBuffer, not shown here) performs abysmally. 并使用sun.misc.Unsafe(以及直接java.nio.FloatBuffer,此处未显示)执行得非常糟糕。


#6楼

Usually not. 通常不是。 For virtual methods, HotSpot keeps track of whether the method has actually been overridden, and is able to perform optimizations such as inlining on the assumption that a method hasn't been overridden - until it loads a class which overrides the method, at which point it can undo (or partially undo) those optimizations. 对于虚方法,HotSpot会跟踪方法是否实际被覆盖,并且能够在假设方法尚未被覆盖的情况执行优化,例如内联 - 直到它加载一个覆盖方法的类,此时它可以撤消(或部分撤消)这些优化。

(Of course, this is assuming you're using HotSpot - but it's by far the most common JVM, so...) (当然,这假设您正在使用HotSpot - 但它是迄今为止最常见的JVM,所以...)

To my mind you should use final based on clear design and readability rather than for performance reasons. 在我看来,你应该使用基于清晰设计和可读性的final而不是出于性能原因。 If you want to change anything for performance reasons, you should perform appropriate measurements before bending the clearest code out of shape - that way you can decide whether any extra performance achieved is worth the poorer readability/design. 如果您出于性能原因想要更改任何内容,则应在将最清晰的代码变形之前执行适当的测量 - 这样您就可以决定是否所获得的额外性能值得更差的可读性/设计。 (In my experience it's almost never worth it; YMMV.) (根据我的经验,它几乎不值得; YMMV。)

EDIT: As final fields have been mentioned, it's worth bringing up that they are often a good idea anyway, in terms of clear design. 编辑:由于已经提到了最后的领域,所以值得提出的是,无论如何,在清晰的设计方面,它们通常都是个好主意。 They also change the guaranteed behaviour in terms of cross-thread visibility: after a constructor has completed, any final fields are guaranteed to be visible in other threads immediately. 它们还在跨线程可见性方面改变了保证行为:在构造函数完成之后,任何最终字段都保证在其他线程中立即可见。 This is probably the most common use of final in my experience, although as a supporter of Josh Bloch's "design for inheritance or prohibit it" rule of thumb, I should probably use final more often for classes... 这可能是我经验中最常用的final ,虽然作为Josh Bloch的“继承设计或禁止它的设计”的支持者,我应该更频繁地使用final来进行课程...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
写在前面的话 引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 7. 多媒体 8. 源代码 9. 编码样式 10. Java版本 11. 课程和培训 12. 错误 13. 封面设计 14. 致谢 第1章 对象入门 1.1 抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础类 1.5.2 等价和类似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 1.6.2 抽象的基础类和接口 1.7 对象的创建和存在时间 1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:解决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段1:要制作什么? 1.12.4 阶段2:开始构建? 1.12.5 阶段3:正式创建 1.12.6 阶段4:校订 1.12.7 计划的回报 1.13 Java还是C++? 第2章 一切都是对象 2.1 用句柄操纵对象 2.2 必须创建所有对象 2.2.1 保存在什么地方 2.2.2 特殊情况:主类型 2.2.3 Java的数组 2.3 绝对不要清除对象 2.3.1 作用域 2.3.2 对象的作用域 2.4 新建数据类型:类 2.4.1 字段和方法 2.5 方法、自变量和返回值 2.5.1 自变量列表 2.6 构建Java程序 2.6.1 名字的可见性 2.6.2 使用其他组件 2.6.3 static关键字 2.7 我们的第一个Java程序 2.8 注释和嵌入文档 2.8.1 注释文档 2.8.2 具体语法 2.8.3 嵌入 2.8.4 @see:引用其他类 2.8.5 类文档标记 2.8.6 变量文档标记 2.8.7 方法文档标记 2.8.8 文档示例 2.9 编码样式 2.10 总结 2.11 练习 第3章 控制程序流程 3.1 使用Java运算符 3.1.1 优先级 3.1.2 赋值 3.1.3 算术运算符 3.1.4 自动递增和递减 3.1.5 关系运算符 3.1.6 逻辑运算符 3.1.7 按位运算符 3.1.8 移位运算符 3.1.9 三元if-else运算符 3.1.10 逗号运算符 3.1.11 字串运算符 3.1.12 运算符常规操作规则 3.1.13 造型运算符 3.1.14 Java没有“sizeof” 3.1.15 复习计算顺序 3.1.16 运算符总结 3.2 执行控制 3.2.1 真和假 3.2.3 反复 3.2.6 断和继续 3.2.7 切换 3.3 总结 3.4 练习 第4章 初始化和清除 4.1 由构建器保证初始化 4.2 方法过载 4.2.1 区分过载方法 4.2.2 主类型的过载 4.2.3 返回值过载 4.2.4 默认构建器 4.2.5 this关键字 4.3 清除:收尾和垃圾收集 4.3.1 finalize()用途何在 4.3.2 必须执行清除 4.4 成员初始化 4.4.1 规定初始化 4.4.2 构建器初始化 4.5 数组初始化 4.5.1 多维数组 4.6 总结 4.7 练习 第5章 隐藏实施过程 5.1 包:库单元 5.1.1 创建独一无二的包名 5.1.2 自定义工具库 5.1.3 利用导入改变行为 5.1.4 包的停用 5.2 Java访问指示符 5.2.1 “友好的” 5.2.2 public:接口访问 5.2.3 private:不能接触 5.2.4 protected:“友好的一种” 5.3 接口与实现 5.4 类访问 5.5 总结 5.6 练习 第6章 类再生 6.1 合成的语法 6.2 继承的语法 6.2.1 初始化基础类 6.3 合成与继承的结合 6.3.1 确保正确的清除 6.3.2 名字的隐藏 6.4 到底选择合成还是继承 6.6 递增开发 6.7 上溯造型 6.7.1 何谓“上溯造型”? 6.8 final关键字 6.8.1 final数据 6.8.2 final方法 6.8.3 final类 6.8.4 final的注意事项 6.9 初始化和类装载 6.9.1 继承初始化 6.10 总结 6.11 练习 第7章 多形性 7.1 上溯造型 7.1.1 为什么要上溯造型 7.2 深入理解 7.2.1 方法调用的绑定 7.2.2 产生正确的行为 7.2.3 扩展性 7.3 覆盖与过载 7.4 抽象类和

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值