Конкатенация Java-строк и интернирование
Вопрос 1
String a1 = "I Love" + " Java";
String a2 = "I Love " + "Java";
System.out.println( a1 == a2 ); // true
String b1 = "I Love";
b1 += " Java";
String b2 = "I Love ";
b2 += "Java";
System.out.println( b1 == b2 ); // false
В первом случае я понимаю, что это конкатенация двух строковых литералов, поэтому результат "Я люблю Java" будет интернирован, что даст результат true. Однако я не уверен насчет второго случая.
вопрос 2
String a1 = "I Love" + " Java"; // line 1
String a2 = "I Love " + "Java"; // line 2
String b1 = "I Love";
b1 += " Java";
String b2 = "I Love ";
b2 += "Java";
String b3 = b1.intern();
System.out.println( b1 == b3 ); // false
Выше возвращается false, но если я закомментирую строки 1 и 2, он возвращает true. Это почему?
3 ответа
Первая часть вашего вопроса проста: компилятор Java рассматривает конкатенацию нескольких строковых литералов как единый строковый литерал, т.е.
"I Love" + " Java"
а также
"I Love Java"
два одинаковых строковых литерала, которые правильно интернированы
Такое же поведение интернирования не относится к +=
операция на строках, так b1
а также b2
на самом деле строятся во время выполнения.
Вторая часть сложнее. Напомним, что b1.intern()
может вернуться b1
или какой-то другой String
объект, который равен ему. Когда вы держите a1
а также a2
, ты получаешь a1
вернуться от звонка b1.intern()
, Когда вы комментируете a1
а также a2
, нет существующей копии для возврата, поэтому b1.intern()
возвращает тебя b1
сам.
Из стажера () документов
Все литеральные строки и строковые константные выражения интернированы. Строковые литералы определены в разделе 3.10.5 Спецификации языка Java™.
И из JLS 3.10.5
- Строки, вычисленные с помощью константных выражений (§15.28), вычисляются во время компиляции, а затем обрабатываются, как если бы они были литералами.
- Строки, вычисленные путем конкатенации во время выполнения, создаются заново и поэтому различаются.
Ваша строка b1 на самом деле не интернирована. Отсюда и разница.
Ответ на вопрос 1:
Вы не можете сравнить две строки с ==
, ==
сравнивает два примитивных типа данных (int, long, float, double и boolean) или ссылки на объекты. Это означает, что если эталонные переменные (a1, a2, b1, b2) не имеют одинаковую ссылку (то есть они не указывают на один и тот же объект в памяти), они не равны (сравнение с ==
).
Если бы вы сравнили с b1.equals(b2)
, выражение будет истинным, поскольку данные объекта совпадают.
В первом случае Java достаточно умен, чтобы объединять строки перед выделением им некоторой памяти (даже перед компиляцией), что означает, что обе строки хранятся по одному адресу. Следовательно, переменные a1 и a2 ссылаются на один и тот же объект и равны (==
).
Во втором случае вы сначала назначаете переменным другое значение (в отличие от первого случая). Это означает, что они получают отдельный адрес в памяти. Даже если вы измените значение, чтобы они были одинаковыми, адрес не изменится и сравнение с ==
оценивается как ложное. Это происходит во время выполнения.
Что касается вопроса 2: @dasblinkenlight уже дал хороший ответ на это.