拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Java final关键字对性能的影响

Java final关键字对性能的影响

白鹭 - 2021-11-24 410 0 0

1.概述

final关键字的性能优势是一个非常热门的辩论主题。根据我们在哪里使用它, final关键字可以具有不同的目的和不同的性能含义

在本教程中,我们将探讨在代码中final我们将研究在变量,方法和类级别final

除了性能,我们还将提到使用final关键字的设计方面。最后,我们将建议是否以及出于什么原因使用它。

2.局部变量

final应用于局部变量时,其值只能必须分配一次

我们可以在声明final变量或类构造函数中分配值。如果我们稍后尝试更改最终变量值,则编译器将引发错误。

2.1 性能测试

让我们看看将final关键字应用于我们的局部变量是否可以提高性能。

我们将使用JMH工具来衡量基准测试方法的平均执行时间。在我们的基准测试方法中,我们将对非最终局部变量进行简单的字符串连接:

@Benchmark

 @OutputTimeUnit(TimeUnit.NANOSECONDS)

 @BenchmarkMode(Mode.AverageTime)

 public static String concatNonFinalStrings() {

 String x = "x";

 String y = "y";

 return x + y;

 }

接下来,我们将重复相同的性能测试,但这一次是使用最终的局部变量:

@Benchmark

 @OutputTimeUnit(TimeUnit.NANOSECONDS)

 @BenchmarkMode(Mode.AverageTime)

 public static String concatFinalStrings() {

 final String x = "x";

 final String y = "y";

 return x + y;

 }

为了让JIT编译器优化生效,JMH将负责运行预热迭代。最后,让我们看一下测量的平均性能(以纳秒为单位):

Benchmark Mode Cnt Score Error Units

 BenchmarkRunner.concatFinalStrings avgt 200 2,976 ± 0,035 ns/op

 BenchmarkRunner.concatNonFinalStrings avgt 200 7,375 ± 0,119 ns/op

在我们的示例中,使用最终局部变量可使执行速度提高2.5倍。

2.2 静态代码优化

字符串连接示例演示final关键字如何帮助编译器静态优化代码

使用非final局部变量,编译器生成以下字节码来连接两个字符串:

NEW java/lang/StringBuilder

 DUP

 INVOKESPECIAL java/lang/StringBuilder.<init> ()V

 ALOAD 0

 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

 ALOAD 1

 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

 INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

 ARETURN

通过添加final关键字,我们帮助编译器得出结论,字符串连接结果实际上永远不会改变。因此,编译器可以完全避免字符串连接,并可以静态优化生成的字节码:

LDC "xy"

 ARETURN

我们应该注意,在大多数情况下,将final添加到我们的局部变量中不会像本例中那样带来显著的性能优势。

3.实例和类变量

我们可以将final关键字应用于实例或类变量。这样,我们确保它们的值分配只能完成一次。我们可以在实例初始化程序块或构造函数中,在最终实例变量声明时分配值。

static关键字添加到类的成员变量来声明类变量。此外,通过将final关键字应用于类变量,我们可以定义一个constant 。我们可以在常量声明时或在静态初始化程序块中分配值:

static final boolean doX = false;

 static final boolean doY = true;

让我们编写一个使用这些boolean常量的条件的简单方法:

Console console = System.console();

 if (doX) {

 console.writer().println("x");

 } else if (doY) {

 console.writer().println("y");

 }

接下来,让我们boolean final关键字,然后比较该类生成的字节码:

  • 使用非最终类变量的示例– 76行字节码
  • 使用最终类变量(常量)的示例– 39行字节码

通过将final关键字添加到类变量中,我们再次帮助编译器执行静态代码优化。编译器将简单地将最终类变量的所有引用替换为其实际值。

但是,我们应该注意,像这样的示例很少在现实的Java应用程序中使用。将变量声明为final只会对实际应用程序的性能产生较小的积极影响。

4.Effectively final

Effectively final变量一词是在Java 8中引入的。如果未明确将变量声明为final变量,但变量的值在初始化后再也不会更改,则实际上是final变量。

有效地使用final变量的主要目的是使lambda能够使用未明确声明为final的局部变量。但是,Java编译器不会像对最终变量那样有效地对Effectively final变量进行静态代码优化。

5.类和方法

当将final关键字应用于类和方法时,其目的不同。当我们将final关键字应用于一个类时,则该类不能被子类化。当我们将其应用于方法时,该方法就不能被覆盖。

没有报告将final应用于类和方法的性能好处。此外,最终类和方法可能给开发人员带来极大的不便,因为它们限制了我们重用现有代码的选择。因此,不顾后果地使用final可能会损害良好的面向对象设计原则。

创建最终类或方法有一些正当的理由,例如强制执行不变性。但是,**性能优势并不是在类和方法级别上final**的充分理由。

6.性能与简洁设计

除了性能之外,我们还可以考虑使用final其他原因。 final关键字可以帮助提高代码的可读性和可理解性。让我们看一些有关final如何传达设计选择的示例:

  • 最后的课程是设计不变的
  • 方法声明为final,以防止子类不兼容
  • 方法参数声明为final,以防止产生副作用
  • 最终变量在设计上是只读的

因此,我们应该使用final将设计选择传达给其他开发人员。此外, final关键字可以作为编译器执行次要性能优化的有用提示。

7.结论

在本文中,我们研究了使用final关键字的性能优势。在示例中,我们显示了final关键字应用于变量可能会对性能产生较小的积极影响。但是,将final关键字应用于类和方法不会带来任何性能上的好处。

我们证明了与最终变量不同,编译器没有有效地使用最终变量来执行静态代码优化。最后,除了性能之外,我们还研究了在不同级别上final

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *