[英]Javac missing optimization for effective final
事實:
javac
被編程為檢測變量是否是final
或者它是否可以被視為有效的 final
變量。
證明:
此代碼說明了這一點。
public static void finalCheck() {
String str1 = "hello";
Runnable r = () -> {
str1 = "hello";
};
}
這無法編譯,因為編譯器能夠檢測到String
引用str1
正在函數中重新分配。
現在
情況1:
通過避免創建StringBuilder
和相關操作,Javac對final
String
實例進行了很好的優化。
證明
這個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
問題:為什么不優化效果最終?
引入有效的最終概念並不影響有關常量表達式和字符串連接的規則。
請參閱Java®語言規范,§15.18.1。 字符串連接運算符+
執行不是常量表達式(第15.28節 )一部分的字符串連接運算符
+
(第15.18.1 節 )總是創建一個新的String
對象來表示結果。
因此,雖然某些構造可能具有可預測的字符串結果,但即使不是常量表達式,用常量結果替換它們也會違反規范。 只有常量表達式可以(事件必須)在編譯時被它們的常量值替換。 關於引用變量,§15.28聲明它們必須是根據§4.12.4的常量變量才是常量表達式:
常量變量是基本類型或類型
String
的final
變量,使用常量表達式(第15.28節 )初始化。
注意對於常量變量的final
要求。
還有隱含最終變量的概念,這與有效最終變量不同:
三種變量被隱式聲明為
final
:接口的一個字段(第9.3節 ),一個聲明為try
-with-resources語句( §14.20.3 )的資源的局部變量,以及一個多catch
的異常參數條款( §14.20 )。 unicatch
子句的異常參數永遠不會被隱式聲明為final
,但可能是有效的final。
因此,沒有太多令人驚訝的是,接口字段是隱式final
(它們也是隱式static
),因為它們總是如此,而另外兩個隱式final
變量的情況永遠不能是字符串也不能是原始類型,因此永遠不會是常量。
有效地,最終變量僅在某些用例中被特別處理(如final
變量)
try
-with-resource引用它們( try(existingVariable) { … }
(自Java 9起) 但除此之外,它們不會被視為final
變量。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.