Во-первых, String.toString
не работает:
/**
* This object (which is already a string!) is itself returned.
*
* @return the string itself.
*/
public String toString() {
return this;
}
Во-вторых, строковые константы интернированы, поэтому s1 и s2 за кулисами изменены на один и тот же экземпляр String.
s2.toString () возвращает строковое представление. Поскольку это уже строка, она возвращает себя (поэтому сравнение верно).
Все строки размещаются в куче, оператор сравнения просто сравнивает, являются ли они одним и тем же объектом (вот почему s1! = S2).
Учитывая, что ==
сравнивает ссылки, вы можете видеть из s2 == s3
, что верно, что s2.toString ()
возвращает s2.
При сравнении строк Java вы должны использовать .equals, а не ==.
== Сравнивает ссылки, поэтому s2.ToString () возвращает s2.
Из глоссария java о куче / стеке:
В JVM Sun интернированные строки (которые включают строковые литералы) являются
хранятся в специальном пуле ОЗУ. называется перманентным генератором, где JVM также загружает классы и магазины код, скомпилированный в собственном коде. Тем не менее интересующие строки ведут себя по-разному чем они хранились в куча обычных объектов.
Метод String.intern () может использоваться, чтобы гарантировать, что равные строки имеют равные ссылки. Строковые константы intern
ed, поэтому s1
и s2
будут ссылаться на одну и ту же строку. String.toString ()
просто возвращает себя, то есть a.toString ()
возвращает a, когда a является строкой. Итак, s2 также == s3.
В общем, строки следует сравнивать не по ссылочному равенству, а по равенству значений, используя equals ()
. Причина в том, что легко получить две эквивалентные, но разные ссылки. Например, при создании подстрок. Исключением из этого правила является то, что если вы знаете, что обе строки были интернированы
заранее (или вы интернировали их как часть сравнения).
Чтобы ответить на ваш подразумеваемый вопрос о куче или стеке, строки являются размещены в куче. Даже если они были размещены в стеке, например, при предстоящем анализе выхода и распределении стека, семантика программы не изменится, и вы получите тот же результат для распределения кучи и стека.
Поскольку String являются неизменяемыми, полезная реализация метода toString - в классе String - возвращать это.
В качестве примера мой rt.jar содержит следующую реализацию:
public String toString() {
return this;
}
Как следствие, ссылка, связанная с s3
, такая же, как ссылка, связанная с s2
.
Учитывая оператор s1 == s2
, это связано с автоматическим вызовом intern ()
для всех константных строк. Это означает, что во время компиляции код инициализации s2 будет заменен на s2 = s1
, что делает утверждение довольно очевидным, не так ли?
Из спецификации виртуальной машины java:
Строковые литералы и, в более общем случае, строки, являющиеся значениями константных выражений, "интернируются", чтобы иметь общие уникальные экземпляры, с помощью метода String.intern.
"andrei"
является строковым литералом и поэтому "интернируется". Поэтому обе строки s1
и s2
ссылаются на один и тот же объект String, интернированную строку с содержимым "andrei"
.
Поэтому (s1 == s2) && (s1.equals(s2))
является true
. String#toString()
не создает новую String
(как многие другие методы из String
), а просто возвращает this
. Поэтому s2 == s3
является true
.