Основной вопрос Java: равенство строк

public class A {

    static String s1 = "I am A";

    public static void main(String[] args) {
        String s2 = "I am A";
        System.out.println(s1 == s2);
    }
}

Вышеуказанная программа выводит «true». Оба являются двумя разными идентификаторами/объектами, как вывод является «истинным»?

Насколько я понимаю, JVM будет создавать разные ссылки для каждого объекта, если да, то как вывод верен?


person novice    schedule 13.12.2009    source источник


Ответы (5)


Java управляет литеральным пулом String. Он повторно использует эти литералы, когда это возможно. Следовательно, два объекта на самом деле являются одним и тем же объектом String, а == возвращает значение true.

Я полагаю, что это называется интернированием строк

person Neil Foley    schedule 13.12.2009
comment
Информацию о пуле строк можно найти в Википедии: en.wikipedia.org/wiki/String_interning (+1 та же идея) - person Felix Kling; 13.12.2009
comment
Интернируются не только литералы, но и константы времени компиляции. - person Michael Borgwardt; 13.12.2009
comment
Только пожалуйста, пожалуйста, пожалуйста... как показывает мой ответ... не зависьте от этого факта. Используйте equals, когда это то, что вы имеете в виду :-). Я всегда нервничаю, когда люди так небрежно публикуют сообщения об интернировании String ... Очевидно, что ответ правильный, и я не оспариваю это, но я хотел бы подчеркнуть, что, хотя это объясняет ответ, это не то, что должно быть полагался (как указывает Джош Блох, это деталь реализации, а не инструмент программиста). - person Tom; 13.12.2009
comment
Что-то обязательное в спецификации языка вряд ли является деталью реализации, но я согласен: хотя вы можете зависеть от интернированных литералов, вы не должны зависеть от того, что строки, которые вы используете, являются литералами - это может слишком легко измениться. - person Michael Borgwardt; 13.12.2009

== проверяет, что переменные указывают на один и тот же экземпляр объекта. Два созданных вами строковых литерала указывают на одно и то же место в памяти и, следовательно, равны. Строковые литералы интернированы, так что один и тот же строковый литерал является одним и тем же объектом в памяти.

Если бы ты сделал

String s = new String("foo");
String t = new String("foo");

Тогда == вернет false, а s.equals(t) вернет true.

person Jeff Foster    schedule 13.12.2009

Поскольку Спецификация языка Java говорит :

Строковые литералы — или, в более общем смысле, строки, являющиеся значениями константных выражений (§15.28), — «интернированы», чтобы совместно использовать уникальные экземпляры, используя метод String.intern.

person Michael Borgwardt    schedule 13.12.2009

Это из-за оптимизации памяти, выполненной компилятором... а именно, константы String (то есть - String, созданные одним и тем же литералом String) используют один и тот же объект String, поскольку Strings неизменяемы. Оператор == просто проверяет, что два объекта являются одним и тем же фактическим объектом.

Если вы возьмете книгу Джошуа Блоха и Нила Гафтера «Java Puzzlers» и посмотрите на головоломку 13 «Скотный двор»… у него есть отличный совет по этому вопросу. Я собираюсь скопировать соответствующий текст:

«Возможно, вы знаете, что константы времени компиляции типа String интернированы [JLS 15.28]. Другими словами, любые два константных выражения типа String, обозначающие одну и ту же последовательность символов, представлены идентичными ссылками на объекты... Ваш код редко должен, если когда-либо, зависит от интернирования строковых констант.Интернирование было разработано исключительно для уменьшения объема памяти виртуальной машины, а не как инструмент для программистов... При сравнении ссылок на объекты вы должны использовать метод equals, а не == оператор, если вам не нужно сравнивать идентификатор объекта, а не значение».

Это из упомянутой выше ссылки... страницы 30-31 в моей книге.

person Tom    schedule 13.12.2009
comment
Я упомянул об этом [при условии, что не упомянул, почему это было сделано], и меня за это проголосовали. - person monksy; 13.12.2009

Вы не сравниваете содержимое строк. Вы только сравниваете ссылки на объект. Вы должны использовать метод equal (который является членом класса String). Либо так, либо вы можете использовать метод compareTo (также в том же классе String), чтобы проверить, равно ли возвращаемое значение нулю.

Обратите внимание, что приведенный выше текст был скорее сильным предложением к исходному состоянию вопроса, поскольку казалось, что ОП не знал о фактическом процессе, происходящем за кулисами.

Другие парни, предполагающие интернализацию, были правы. Чтобы ответить на этот вопрос, мне не хватило времени обратиться к книге Java Puzzlers. Я что-то подозревал об установке той же ссылки во время компиляции, но я тоже не знал, как найти ссылку на это.

person monksy    schedule 13.12.2009
comment
Если я сравню содержимое, то ответ определенно верный. Но здесь обе две разные переменные, не создаст ли jvm две разные ссылки? - person novice; 13.12.2009
comment
@Феликс. Нет, он сравнивает ссылки, и результат верен — это как раз тот слегка неожиданный результат, который вызвал этот вопрос. - person Michael Borgwardt; 13.12.2009
comment
@Steve De Caux: я не голосовал против, но я думаю, что люди проголосовали против, потому что это не объясняет, почему ссылки на строки одинаковы. Я думаю, что OP знает разницу между == и equals, но был сбит с толку тем, что два строковых объекта обрабатывались как один и тот же. Этот ответ, хотя и имеет правильное содержание, вообще не отвечает на вопрос. - person Tom; 13.12.2009
comment
Мне кажется странным, когда этого парня минусуют за болтовню, близкую к теме, когда совершенно неправильный ответ на самом деле получил несколько голосов, когда я впервые прочитал этот вопрос. - person Steve De Caux; 13.12.2009
comment
@ Стив: я так и думал. ИМО: отрицательные голоса говорят о неправильном содержании. Думаю, люди не понимают, что отрицательное голосование стоит репутации. - person monksy; 13.12.2009
comment
@ Стив Де Ко: я знаю :-). Такие ответы я обычно просто оставляю в покое. Автор подобных ответов, как правило, просто неправильно понимает вопрос или не понимает, в чем заключается важная часть вопроса. Я склонен оставлять свои отрицательные голоса для сообщений, которые содержат совершенно неверную информацию или далеки от цели (например, ответ Павла). - person Tom; 13.12.2009
comment
Проверьте изменения ... Содержание вопроса изменилось после того, как я опубликовал это. - person monksy; 13.12.2009
comment
@steven: я думаю, что вопрос был ясен с первого раза. ОП спрашивал, почему вывод был верным (он казался удивленным этим, потому что у него было две разные переменные... он знал разницу между равными и ==... просто не знал, почему две разные строки были ==) . - person Tom; 13.12.2009
comment
Что меня расстраивает, так это то, что теперь, когда люди заходят на эту страницу (по большей части), они будут голосовать только за принятый ответ. Довольно сложно представить хорошо продуманный или хорошо написанный ответ, когда он приходит после того, как другой ответ уже получил кучу голосов или был принят. - person Tom; 13.12.2009
comment
Я хочу сказать, что во время первой итерации вопроса он не упомянул, что понимает дело со ссылками, поэтому я вмешался и упомянул, что речь идет о ссылках... Затем я вышел, чтобы проверить, почему этот угловой случай может существовать . - person monksy; 13.12.2009
comment
+1 Я думаю, что ответ правильный, по крайней мере, на исходный вопрос. И все еще действителен для фактической версии вопроса, возможно, не прямого ответа, но, по крайней мере, дополнения для тех читателей, которые не знают правильного использования == и equals. Возможно, @steven должен отредактировать ответ, чтобы отразить это (и разрешить удаление старых отрицательных голосов) - person user85421; 13.12.2009