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

例如:

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

在上面的例子中,str可以是final,但通常不使用。

当一个方法永远不会被覆盖时,我们可以使用final关键字。类似地,类不会被继承。

在任何或所有这些情况下使用final关键字真的能提高性能吗?如果是,那该怎么做?请解释一下。如果final的正确使用确实关系到性能,那么Java程序员应该养成什么样的习惯才能最好地利用这个关键字呢?


当前回答

通常不会。对于虚方法,HotSpot会跟踪该方法是否实际被重写,并且能够在假定某个方法没有被重写的情况下执行优化,例如内联——直到它加载一个重写该方法的类,这时它可以撤销(或部分撤销)那些优化。

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

在我看来,使用final应该基于清晰的设计和可读性,而不是出于性能考虑。如果您出于性能原因想要更改任何内容,那么应该在修改最清晰的代码之前执行适当的度量——这样您就可以决定以较差的可读性/设计换取额外的性能是否值得。(根据我的经验,这几乎不值得;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...

其他回答

注意:不是java专家

如果我没有记错我的java,使用final关键字几乎没有办法提高性能。 我一直知道它的存在是为了“好代码”——设计和可读性。

根据IBM的说法——它不用于类或方法。

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

你实际上在问两种(至少)不同的情况:

局部变量的Final 方法/类的Final

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

我不认为这有什么区别;对于局部变量,编译器可以推断该变量是否是final变量(只需检查它是否被多次赋值)。因此,如果编译器想要优化只赋值一次的变量,无论变量是否声明为final,它都可以这样做。

Final可能会对protected/public类字段产生影响;在那里,编译器很难发现字段是否被设置了不止一次,因为它可能发生在不同的类中(甚至可能没有被加载)。但即使这样,JVM也可以使用Jon描述的技术(乐观地优化,如果加载了更改字段的类,则恢复)。

总之,我看不出有任何理由它应该有助于性能。 所以这种微观优化不太可能有帮助。您可以尝试对其进行基准测试以确定,但我怀疑它是否会产生影响。

编辑:

实际上,根据Timo Westkämper的回答,final在某些情况下可以提高类字段的性能。我接受纠正。

实际上,在测试一些opengl相关代码时,我发现在私有字段上使用final修饰符会降低性能。下面是我测试的类的开头:

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

这是我用来测试各种替代方案的性能的方法,其中包括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

注意ShaderInput测试的图。

将字段设置为公共或私有并不重要。

顺便说一句,还有一些更令人困惑的事情。ShaderInput类的性能优于所有其他变量,即使是final关键字。这是非常了不起的b/c,它基本上是一个包装浮点数组的类,而其他测试直接操作数组。必须把这个弄清楚。可能与ShaderInput的流畅界面有关。

同时系统。arrayCopy对于小数组显然比在for循环中简单地将元素从一个数组复制到另一个数组要慢一些。使用sun.misc.Unsafe(以及直接使用java.nio。FloatBuffer(这里没有显示)执行得非常糟糕。

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

一门课是最终的 引用变量是最终变量 局部变量是final 方法是最终的

类是最终的:类是最终的意味着我们不能被扩展,继承意味着继承是不可能的。

类似地-一个对象是最终对象:有时我们不修改对象的内部状态,所以在这种情况下,我们可以指定对象是最终对象。Object final意味着不是变量也是final。

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