始终使用Final关键字?

4

我读到过,将某些东西标记为final并在循环中使用会带来更好的性能,但这对所有情况都适用吗?我有许多地方没有循环,但我把局部变量标记为final。它会变慢吗,还是仍然可以的?

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

9个回答

7
你应该考虑的第一件事是:我能以何种最简单和最清晰的方式编写这段代码。通常这样做效果很好。
在本地声明的final变量不太可能对性能产生影响。当你有长方法时,它们可以提高可读性,但我建议将方法拆分为更好的解决方案。 final字段可能会对性能产生轻微影响,但使其成为final的更好原因是表明该字段永远不会更改(这也有助于JIT)。

我想补充一点,字段上的 final 和局部变量上的 final 是非常不同的。在并发编程中,将字段设置为 final 通常是确保程序正确性的绝对必要条件。 - Enno Shioji
除了将一个新对象传递给另一个线程的相对微妙的条件(我没有看到这种情况失败的例子),如果一个字段本来可以是final但实际上不是,它的行为将是相同的。将其设置为final可以明确表明原始类型或引用不会有线程安全问题,因为它不能被更改。注意:所引用的对象仍然可能会发生变化。 - Peter Lawrey
我只会留下规范的链接。 最终变量:http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.12.4 最终字段:http://java.sun.com/docs/books/jls/third_edition/html/memory.html#66562 简而言之:前者对文档很有用,后者可以显着改变程序的行为。 - Enno Shioji

4
不要考虑性能。对象成员(字段)上的final具有重要的内存语义,可以提高性能(但更重要的是,通常需要使代码正确工作)。您应该尽可能在对象成员上使用final。然而,对于局部变量,只有在它能提高代码可读性或者当维护者接触到您的代码时可以防止错误时才应使用它
Java社区的普遍共识是每个局部变量都加上final会使代码难以阅读。在性能方面,由于局部变量易于分析,因此您不应期望进行任何优化。换句话说,编译器可以自行解决这个问题。

3

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

然而,这样看起来非常丑陋。这就是我反对它的主要原因。

如果程序的某个部分不是关键性能部分,请谨防过早优化。


2
这里是我的意见:
在属性上使用final可以最大程度地减少可变性,并用于文档编写。只有当局部变量被用于内部/匿名类时,才在局部变量上使用final。 不要为微小的优化使用它!特别是不要在类或方法上使用它,因为您认为它会提高性能。将类和方法设为final,以禁止继承或重写方法。

2

在可行的情况下(对于字段和变量,而不是类和方法),使用final被认为是一种良好的编程风格,因为这样可以使测试更加容易。final永远不会对性能产生负面影响。


1

在性能方面,属性上的final不应该有任何影响。除非:在多线程环境中,多个线程访问同一字段并且“不知道”是否需要重新加载它。对于局部变量的final没有任何影响,因为除了局部作用域之外,没有其他东西可以访问它们。

方法上的final可能会在JIT编译期间产生影响。如果一个方法是final和小的,编译器可以将其内联到循环中,因为明确知道没有人会覆盖它。

我通常根本不使用属性上的final,因为final属性不能轻松地从数据库中加载等。将参数声明为final看起来很丑陋(我从不在代码中分配给它们),但可能会防止由于打字错误而导致的简单错误。然而,如果您开始为变量使用适当的名称,则不太可能犯这样的打字错误。


0

0
理论上,如果将局部变量设为final,则可以进行优化。但我认为自己将它们设为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专家。


0

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

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

抱歉,您的意思是您不需要:

final int somefinalvalue = 0;

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

还是怎么样?请记住,如果您声明了与全局变量同名的本地变量,那么它将“遮蔽”全局变量。也就是说,它实际上是完全不同的变量。如果您已经有了全局变量,请直接使用它,无需重新声明。


我认为他的意思是对字段进行本地“缓存”。 - Bart van Heukelom
在Java中,它们是“不可变的”,但不是像编译器知道它将具有什么值一样“常量”。编译器只是禁止分配操作。 - marc
最终变量在C++常量的意义上不是“恒定”的。编译器 不会 优化它。如果这样做,派生类将无法再读取该字段。编译器不应该那么聪明,因为这是由JIT编译器来完成的! - Angel O'Sphere
没错,但静态字段是“4”,编译器不会生成一个“load 4”和“compare”指令对,而是执行一个“load static field”的指令。 - Angel O'Sphere
@Angel O'Sphere:我应该说JIT,我的意思是真正的JIT,而不是字节码编译器。谢谢你澄清了这一点 :) - LeleDumbo
显示剩余3条评论

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