У меня есть простой вопрос о строках в Java. Следующий фрагмент простого кода просто объединяет две строки и затем сравнивает их с ==
.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Выражение сравнения concat=="string"
возвращается false
как очевидное (я понимаю разницу между equals()
и ==
).
Когда эти две строки объявлены final
так,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Выражение сравнения concat=="string"
в этом случае возвращает true
. Почему имеет final
значение? Это как-то связано со стажировкой или меня просто вводят в заблуждение?
equals()
и==
в контексте строк и задает более значимый вопрос.String
? Я думаю, что вполне логично, а не глупо проводить сравнение содержимогоequals
, метод, который мы можем переопределить, чтобы сообщить, когда мы считаем два объекта равными, и выполнить сравнение идентификаторов==
. Если бы сравнение содержимого было выполнено,==
мы не могли бы переопределить это, чтобы определить, что мы подразумеваем под «равным содержанием», и иметь значениеequals
и==
обращенное только дляString
s было бы глупо. Кроме того, независимо от этого, я не вижу никакого преимущества в том==
, чтобы сравнивать содержимое вместоequals
.Ответы:
Когда вы объявляете
String
(которая является неизменной ) переменную какfinal
, и инициализируете ее с помощью константного выражения во время компиляции, она также становится константным выражением во время компиляции, и ее значение указывается компилятором, где она используется. Итак, во втором примере кода после вставки значений компиляция строк преобразуется компилятором в:который по сравнению с
"string"
даст вамtrue
, потому что строковые литералы интернированы .Из JLS §4.12.4 -
final
Переменные :Также из JLS §15.28 - Выражение константы:
Это не тот случай в вашем первом примере кода, где
String
переменные отсутствуютfinal
. Таким образом, они не являются константными выражениями времени компиляции. Операция конкатенации будет отложена до времени выполнения, что приведет к созданию новогоString
объекта. Вы можете проверить это, сравнив байт-код обоих кодов.Первый пример кода (не
final
версия) компилируется в следующий байт-код:Ясно, что он хранится
str
иing
в двух отдельных переменных, и используетсяStringBuilder
для выполнения операции конкатенации.Принимая во внимание, что ваш второй пример кода (
final
версия) выглядит так:Таким образом, он напрямую указывает на конечную переменную для создания String
string
во время компиляции, которая загружаетсяldc
операцией в шаге0
. Затем второй строковый литерал загружаетсяldc
операцией в шаге7
. Это не предполагает создания какого-либо новогоString
объекта во время выполнения. Строка уже известна во время компиляции, и они интернированы.источник
true
?String
Согласно моему исследованию, все
final String
интернированы в Java. Из одного сообщения в блоге:Так что это означает, что если вы позвоните,
String.intern()
вы можете сравнить две строки, используя==
оператор. Но здесьString.intern()
нет необходимости, потому что в Javafinal String
внутренне интернированы.Вы можете найти дополнительную информацию для сравнения строк, используя оператор == и Javadoc для метода String.intern () .
Также обратитесь к этому сообщению Stackoverflow для получения дополнительной информации.
источник
Если вы посмотрите на эти методы
и его декомпилируется с
javap -c ClassWithTheseMethods
версиями, которые вы увидитеи
Так что, если строки не являются окончательными, компилятор должен будет использовать
StringBuilder
для объединенияstr1
иstr2
такбудет скомпилировано в
Это означает, что
concat
он будет создан во время выполнения и не будет поступать из пула строк.Кроме того, если строки являются окончательными, то компилятор может предположить, что они никогда не изменятся, поэтому вместо использования
StringBuilder
он может безопасно объединить свои значения так,можно изменить на
и соединены в
Это означает, что
concate
он станет строковым литералом, который будет интернирован в пул строк, а затем сравнивается с тем же строковым литералом из этого пула вif
выражении.источник
Концепция пула стековых и строковых контентов
источник
Давайте посмотрим некоторый байт-код для
final
примераВ
0:
и2:
,String
"string"
он помещается в стек (из пула констант) и сохраняетсяconcat
непосредственно в локальной переменной . Вы можете сделать вывод, что компилятор создает (объединяет)String
"string"
сам во время компиляции.Не
final
байт - кодЗдесь у вас есть две
String
константы,"str"
и"ing"
которые должны быть объединены во время выполнения сStringBuilder
.источник
Хотя, когда вы создаете с использованием строковой литеральной нотации Java, он автоматически вызывает метод intern () для помещения этого объекта в пул строк, если он еще не присутствовал в пуле.
Компилятор знает, что окончательная переменная никогда не изменится, когда мы добавим эти окончательные переменные, выходные данные отправляются в String Pool из-за того, что
str1 + str2
выходные данные выражения также никогда не изменятся, поэтому, в конце концов, компилятор вызывает метод inter после вывода двух вышеуказанных последних переменных. В случае не финальной переменной компилятор не вызывайте метод intern.источник