Почему неизменяемая новая строка ("рыба")!= Новая строка ("рыба")?

Я помню, как читал раздел, возможно, в " Эффективной 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
Другие вопросы по тегам