Когда вы объявляете String(которая является неизменной ) переменную как final, и инициализируете ее с помощью константного выражения во время компиляции, она также становится константным выражением во время компиляции, и ее значение указывается компилятором, где она используется. Итак, во втором примере кода после вставки значений компиляция строк преобразуется компилятором в:
String concat = "str" + "ing"; // which then becomes `String concat = "string";`
который по сравнению с "string"даст вам true, потому что строковые литералы интернированы .
Из JLS §4.12.4 - finalПеременные :
Переменная примитивного типа или типа String, которая finalинициализируется константным выражением времени компиляции (§15.28), называется константной переменной .
Также из JLS §15.28 - Выражение константы:
Постоянные выражения типа времени компиляции Stringвсегда "интернированы", чтобы использовать уникальные экземпляры, используя метод String#intern().
Это не тот случай в вашем первом примере кода, где Stringпеременные отсутствуют final. Таким образом, они не являются константными выражениями времени компиляции. Операция конкатенации будет отложена до времени выполнения, что приведет к созданию нового Stringобъекта. Вы можете проверить это, сравнив байт-код обоих кодов.
Первый пример кода (не finalверсия) компилируется в следующий байт-код:
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
Ясно, что он хранится strи ingв двух отдельных переменных, и используется StringBuilderдля выполнения операции конкатенации.
Принимая во внимание, что ваш второй пример кода ( finalверсия) выглядит так:
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
Таким образом, он напрямую указывает на конечную переменную для создания String stringво время компиляции, которая загружается ldcоперацией в шаге 0. Затем второй строковый литерал загружается ldcоперацией в шаге 7. Это не предполагает создания какого-либо нового Stringобъекта во время выполнения. Строка уже известна во время компиляции, и они интернированы.