Gdy zadeklarujesz zmienną String(która jest niezmienna ) jako finali zainicjujesz ją wyrażeniem stałym czasu kompilacji, stanie się ono również wyrażeniem stałym czasu kompilacji, a jego wartość zostanie podkreślona przez kompilator, w którym jest używana. Zatem w drugim przykładzie kodu, po wstawieniu wartości, kompilacja łańcucha jest tłumaczona przez kompilator na:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
co w porównaniu do "string"da ci true, ponieważ literały łańcuchowe są internowane .
Z JLS §4.12.4 - finalZmienne :
Zmienna pierwotnego typu lub typu String, która jest finalinicjowana za pomocą wyrażenia stałego w czasie kompilacji (§ 15.28), nazywana jest zmienną stałą .
Również z JLS § 15.28 - Wyrażenie stałe:
Wyrażenia typu stałego w czasie kompilacji Stringsą zawsze „internowane”, tak aby współużytkować unikalne instancje przy użyciu tej metody String#intern().
Nie jest tak w pierwszym przykładzie kodu, w którym Stringzmienne nie są final. Nie są to zatem wyrażenia stałe czasu kompilacji. Operacja konkatenacji zostanie opóźniona do czasu wykonania, co spowoduje utworzenie nowego Stringobiektu. Możesz to sprawdzić, porównując kod bajtowy obu kodów.
Pierwszy przykład kodu (nie w finalwersji) jest kompilowany do następującego kodu bajtowego:
Code:
0: ldc #2; //String str
2: astore_1
3: ldc #3; //String ing
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
28: aload_3
29: ldc #9; //String string
31: if_acmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #10; //Method java/io/PrintStream.println:(Z)V
42: return
Oczywiste jest przechowywanie stri ingw dwóch różnych zmiennych, a przy użyciu StringBuilderwykonać operację konkatenacji.
Natomiast twój drugi przykład kodu ( finalwersja) wygląda następująco:
Code:
0: ldc #2; //String string
2: astore_3
3: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_3
7: ldc #2; //String string
9: if_acmpne 16
12: iconst_1
13: goto 17
16: iconst_0
17: invokevirtual #4; //Method java/io/PrintStream.println:(Z)V
20: return
Więc bezpośrednio wstawia ostatnią zmienną do utworzenia ciągu stringw czasie kompilacji, który jest ładowany przez ldcoperację w kroku 0. Następnie drugi ldckrok literału jest ładowany przez operację w kroku 7. Nie wymaga tworzenia żadnego nowego Stringobiektu w czasie wykonywania. Łańcuch jest już znany w czasie kompilacji i jest internowany.