Почему неизменяемая новая строка ("рыба")!= Новая строка ("рыба")?
Я помню, как читал раздел, возможно, в " Эффективной Java" Блоха, где говорилось, что в большинстве случаев, где
String a = "fish";
String b = "fish";
что a == b в большинстве случаев, потому что строки неизменны. Но из-за временного построения объектов или чего-то подобного новая String("рыба") будет давать отдельную ссылку на объект.
Я просмотрел главы Блоха об equals(), неизменности и создании объектов, но не могу найти этот бит, который я помню!! Вырывая мои волосы, кто-нибудь помнит, где описание того, почему это? Это может даже не быть в EJ, но я хотел бы найти это. Подсказка: где это объясняется - мой актуальный вопрос.
3 ответа
Это не связано с неизменностью. Это способ, которым строки обрабатываются JVM. Строковый литерал с одинаковым содержимым представляет один и тот же объект ("строковый литерал" означает примерно "текст, окруженный кавычками"). В JVM есть таблица строковых объектов, и у каждого строкового литерала есть ровно один объект в этой таблице.
Однако, когда вы точно создаете новый экземпляр, вы создаете новый строковый объект на основе строкового объекта, взятого из таблицы.
Из любой строки, сформированной без использования литерала (но с помощью вызова toString(), создания экземпляров и т. Д.), Вы можете получить объект из таблицы jvm, вызвав str.intern()
, intern()
Метод возвращает ровно один экземпляр для каждой существующей последовательности символов. new String("fish").intern()
вернет тот же экземпляр, что и просто String s = "fish"
Есть две вещи, которые нужно запомнить:
- никогда не использовать
new String("something")
- всегда сравнивайте строки с
equals(..)
(если вы действительно не знаете, что делаете, и не задокументируете это)
Я думаю, что вы ищете метод String.intern (), который поддерживает постоянный пул строк.
Оператор '==' сравнивает ссылки на объекты (адреса), в то время как.equals () является вызовом метода, который смотрит на семантическую эквивалентность.
Компилятор будет смотреть на String a = "fish" и String b = "fish", а затем может указывать или не указывать на один и тот же адрес. Однако, если вы делаете a.intern(); b.intern(), тогда он, вероятно, поместит их в тот же пул строк и a == b.
Если вы ищете точное описание, перейдите к определению: JLS § 3.10.5 Строковые литералы.
Пример кода, с которым вы должны быть знакомы,
Таким образом, тестовая программа, состоящая из модуля компиляции (§7.3):
package testPackage; class Test { public static void main(String[] args) { String hello = "Hello", lo = "lo"; System.out.print((hello == "Hello") + " "); System.out.print((Other.hello == hello) + " "); System.out.print((other.Other.hello == hello) + " "); System.out.print((hello == ("Hel"+"lo")) + " "); System.out.print((hello == ("Hel"+lo)) + " "); System.out.println(hello == ("Hel"+lo).intern()); } } class Other { static String hello = "Hello"; }
и блок компиляции:
package other; public class Other { static String hello = "Hello"; }
производит вывод:
true true true true false true