事实:
javac
被编程成可以检测一个变量是否是final
,或者它是否可以被视为有效的final
。
证明:
这段代码展示了这一点。
public static void finalCheck() {
String str1 = "hello";
Runnable r = () -> {
str1 = "hello";
};
}
这段代码无法编译,因为编译器能够检测到函数中重新分配了 String
引用 str1
。
现在有一个情况:
情况1:
Javac 对于 final
的 String
实例进行了优化,避免创建 StringBuilder
和相关操作。
证明:
以下是这个 Java 方法的代码
public static void finalCheck() {
final String str1 = "hello";
final String str2 = "world";
String str3 = str1 + " " + str2;
System.out.println(str3);
}
编译结果为
public static void finalCheck();
Code:
0: ldc #3 // String hello world
2: astore_2
3: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_2
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
问题:
但是现在当我们将它们有效地作为 final
public static void finalCheck() {
String str1 = "hello";
String str2 = "world";
String str3 = str1 + " " + str2;
System.out.println(str3);
}
它不会以相似的方式进行优化,最终会编译成。
public static void finalCheck();
Code:
0: ldc #3 // String hello
2: astore_0
3: ldc #4 // String world
5: astore_1
6: aload_0
7: aload_1
8: invokedynamic #5, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
13: astore_2
14: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_2
18: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
21: return
JVM
$java -version
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
编译器
$javac -version
javac 10
问题:为什么它不优化有效最终结果?
javac
不支持这样做是因为它被规定为不支持。然而,我认为jit编译器能够处理这个问题,从而使得两个示例在效率上相等。然而,要证明这一点,我们需要一个微基准测试。 - CoronAjavac
展示这两种情况的行为。我添加了答案... - Holger