【问题标题】:Always Use Final?总是使用最终?
【发布时间】:2011-07-26 08:51:28
【问题描述】:

我已经读过,将某些东西做成最终的,然后在循环中使用它会带来更好的性能,但它对一切都有好处吗?我有很多地方没有循环,但我将 final 添加到局部变量中。它会使其变慢还是仍然很好?

还有一些地方我有一个全局变量 final(例如 android paint),这是否意味着我在循环中使用它时不必将它设为本地 final?

【问题讨论】:

    标签: java loops final


    【解决方案1】:

    您应该考虑的第一件事是;我可以编写此代码的最简单和最清晰的方法是什么。这通常表现良好。

    final 局部变量不太可能影响性能。当你有很长的方法时,它们可以帮助你清晰,但我建议分解方法是更好的方法。

    final 字段可能会在一定程度上影响性能,但使用final 的更好理由是明确该字段永远不会更改(这也有助于 JIT)

    【讨论】:

    • 我要补充一点,字段上的final 与局部变量上的final 非常不同。在并发编程中,为了实现程序的正确性,将字段设为 final 通常是绝对必要的。
    • 除了将新对象传递给另一个线程的相当微妙的条件,(我还没有看到这个失败的例子)如果一个字段可能是 final 但不是,它会行为相同。使它final 清楚地表明原语或引用不会存在线程安全问题,因为它无法更改。注意:引用的对象仍然可以更改。
    • 我只留下规范的链接。最终变量:java.sun.com/docs/books/jls/third_edition/html/…最终字段:java.sun.com/docs/books/jls/third_edition/html/… tl;dr:前者对文档很有用,后者可以显着改变程序的行为。
    【解决方案2】:

    不要考虑性能。对象成员(字段)上的final 具有重要的内存语义,可以提高性能(但更重要的是,它通常需要使代码完全正常工作)。您应该始终将final 放在对象成员上。然而,对于局部变量,您应该只在它可以提高代码可读性或可以防止维护人员接触您的代码时出现错误的情况下使用它。

    Java 社区的普遍共识是每个局部变量上的final 会使代码难以阅读。在性能方面,您可以期望没有优化,因为局部变量很容易为编译器分析。换句话说,编译器可以自己解决。

    【讨论】:

      【解决方案3】:

      根据我的经验,大多数变量都可以声明为final。

      但是,它看起来很丑。这是我反对它的主要观点。

      如果程序的部分不是性能关键,请注意过早的优化。

      【讨论】:

        【解决方案4】:

        在可能的情况下使用 final 被认为是一种很好的形式(对于字段和变量,而不是类和方法),如果没有其他原因,它会使测试更容易。 Final 永远不会对性能产生负面影响。

        【讨论】:

          【解决方案5】:

          这是我的 2 美分:

          在属性上使用 final 以最大程度地减少可变性,并且出于文档目的,仅当局部变量用于内部/匿名类时才对它们使用 final。

          不要将其用于微优化!特别是不要在类或方法上使用它们,因为您认为它会提高性能。使类和方法最终禁止继承或覆盖方法。

          【讨论】:

            【解决方案6】:

            Final 属性不应该对性能产生任何影响。除了:在多线程环境中,多个线程访问同一个字段并且“不知道”是否必须重新加载它。局部变量的 final 根本没有影响,因为除了局部范围之外没有任何东西可以访问它们。 Final on 方法可能会在 JIT 编译期间产生影响。如果一个方法是最终的并且很小,编译器可以在循环中内联它,因为很明显没有人会覆盖它。 我通常根本不使用 final 属性,因为 final 属性不能很容易地从 DB 中加载等等。将参数声明给方法 final lokos 丑陋(无论如何我从来没有在我的代码中分配给它们)但可能会防止来自拼写错误的简单错误.然而,如果你开始为你的变量使用正确的名称,你就不会犯这样的错字。

            【讨论】:

              【解决方案7】:

              理论上,如果你将一个局部变量设为final,它就可以被优化。不过,我不认为让它们最终自己真正提高性能,因为优化器可能已经检测到你的本地人什么时候没有改变。话虽如此,帮一点忙也无妨。

              在某些情况下,将一个变量变为两个变量会有所帮助,例如从此

              String a = "foo";
              if (lol) a += "bar";
              
              for(.. 1000 ...) doSomething(a);
              

              到

              final String a;
              {
                  String ma = "foo";
                  if (lol) ma += "bar";
                  a = ma;
              }
              
              for(.. 1000 ...) doSomething(a);
              

              免责声明:我不是 JIT 专家。

              【讨论】:

                【解决方案8】:

                最终变量是常量,因此编译器可以生成常量值而不是变量引用指令。当然,这会提高速度(通常也会提高尺寸)。

                还有一些地方我有一个全局变量 final(例如 android paint),这是否意味着我在循环中使用它时不必将它设为本地 final?

                对不起,你的意思是你不必:

                final int somefinalvalue = 0;
                
                void amethod() {
                  final int somefinalvalue = 0; // repeated from global one
                }
                

                还是什么?请记住,如果您声明与全局变量同名的局部变量,则会“遮蔽”全局变量。即它实际上是一个完全不同的变量。如果您已经拥有全球性的,那就使用它。无需重新声明。

                【讨论】:

                • 我认为他的意思是字段的本地“缓存”。
                • 在java中它们是不可变的,但不是常量,就好像编译器知道它会有什么值一样。编译器只是禁止赋值。
                • @Marc 这就是为什么我说编译器“可以”,而不是“会”。如果编译器足够聪明,通过一些分析,它可以决定是否将其优化为常量(可能仅适用于整数或浮点等简单值)。
                • 是的,对于原语实际上是可能的,但只是因为它们是值,所以 1 == 1 与 new Object() == new Object() 相反。
                • 最终变量在 c++ 常量的意义上不是“常量”。并且编译器不会优化它。如果可以,派生类将无法再读取该字段。编译器不应该那么聪明,因为这是由 JIT 编译器完成的!!
                【解决方案9】:

                正如@perter-lawrey 所说,我认为这不应该是您首先关心的问题。首先,编译器优化可以起到很大的作用;其次,有一些工具可以分析你生成的类文件并做同样的事情,例如ProGuard: java shrinker, optimizer, obfuscator, and preverifier.

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-03-14
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-03-21
                  • 2014-02-16
                  • 1970-01-01
                  • 2017-05-29
                  相关资源
                  最近更新 更多