Как конкатенация окончательных строк выполняется на Java?
Когда я компилирую этот фрагмент.
public class InternTest {
public static void main(String...strings ){
final String str1="str";
final String str2="ing";
String str= str1+str2;
}
}
Что создает следующий байтовый код
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=1, locals=4, args_size=1
0: ldc #16 // String str
2: astore_1
3: ldc #18 // String ing
5: astore_2
6: ldc #20 // String string
8: astore_3
9: return
поэтому строковая литерал "string" уже присутствует в пуле констант, который нажимается на 6: ldc #20 // String string
на стеке в этой строке.
Цитирование JSL
Из JLS §4.12.4 - окончательные переменные:
Переменная примитивного типа или типа String, которая является окончательной и инициализированное выражением константы времени компиляции (§15.28), является называемой постоянной переменной.
Также из JLS §15.28 - ConstantExpression:
Выражения константы времени компиляции типа String всегда "интернированы" чтобы обмениваться уникальными экземплярами, используя метод String # intern().
Итак, я знаю, что str1 и str2 будут интернированы с момента его создания. "str" и "ing" будут иметь одну и ту же память в строке String str= str1+str2;
Но как str1 + str2 напрямую создает строку в пуле строк. Без вызова любого класса String Builder, как это происходит, когда я не пишу final
.? Чтобы узнать, есть ли у него какие-либо действия в отношении стажеров.
Я написал этот фрагмент
public class IntermTest {
public static void main(String...strings ){
String str1=("str").intern();
String str2=("ing").intern();
String str= str1+str2;
}
}
Но когда я сгенерировал байтовый код, я получил этот
public static void main(java.lang.String...);
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=3, locals=4, args_size=1
0: ldc #16 // String str
2: invokevirtual #18 // Method java/lang/String.intern:
()Ljava/lang/String;
5: astore_1
6: ldc #24 // String ing
8: invokevirtual #18 // Method java/lang/String.intern:
()Ljava/lang/String;
11: astore_2
12: new #26 // class java/lang/StringBuilder
15: dup
16: aload_1
17: invokestatic #28 // Method java/lang/String.valueOf
:(Ljava/lang/Object;)Ljava/lang/String;
20: invokespecial #32 // Method java/lang/StringBuilder.
"<init>":(Ljava/lang/String;)V
23: aload_2
24: invokevirtual #35 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #39 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
30: astore_3
31: return
В действительности он также использует stringBuilder
для конкатенации. Так что он сделал что-то с финалом. Есть ли что-то особенное в строках final
, о которых я точно не знаю?
Ответы
Ответ 1
http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28 говорит, что
Простые имена (§6.5.6.1), которые относятся к постоянным переменным (§4.12.4), являются постоянными выражениями.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28 также говорит:
Постоянное выражение представляет собой выражение, обозначающее значение примитивного типа или String, которое не завершается внезапно и составлено с использованием только следующего:
- Литералы примитивного типа и литералы типа String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)
- [...]
- Аддитивные операторы + и - (§15.18)
- [...]
- Простые имена (§6.5.6.1), которые относятся к постоянным переменным (§4.12.4).
Пример 15.28-1. Константные выражения
[...]
"Целое число + Long.MAX_VALUE +" очень велико. "
Поскольку эти две переменные являются постоянными выражениями, компилятор делает конкатенацию:
String str = str1 + str2;
скомпилируется так же, как
String str = "str" + "ing";
который скомпилирован так же, как
String str = "string";