Концепция называется «интернированием» JLS.
Соответствующий отрывок из JLS 7 3.10.5 :
Кроме того, строковый литерал всегда ссылается на тот же экземпляр класса String. Это связано с тем, что строковые литералы, или, в более общем смысле, строки, которые являются значениями константных выражений (§15.28), «интернированы», чтобы обмениваться уникальными экземплярами, используя метод String.intern.
Пример 3.10.5-1. Строковые литералы
Программа, состоящая из блока компиляции (§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 { public static String hello = "Hello"; }
создает вывод:
blockquote>true true true true false true
JVMS
Строковый литерал является ссылкой к экземпляру класса String и выводится из структуры CONSTANT_String_info (§4.4.3) в двоичном представлении класса или интерфейса. Структура CONSTANT_String_info дает последовательность кодовых точек Unicode, составляющих строковый литерал.
Язык программирования Java требует, чтобы идентичные строковые литералы (то есть литералы, которые содержат одну и ту же последовательность кодовых точек) должны относиться к тому же экземпляр класса String (JLS §3.10.5). Кроме того, если метод String.intern вызывается в любой строке, результатом является ссылка на тот же экземпляр класса, который будет возвращен, если эта строка появилась как литерал. Таким образом, следующее выражение должно иметь значение true:
("a" + "b" + "c").intern() == "abc"
Чтобы получить строковый литерал, виртуальная машина Java проверяет последовательность кодовых точек, заданную структурой CONSTANT_String_info.
blockquote>
- Если метод String.intern ранее был вызван в экземпляр класса String, содержащий последовательность кодовых точек Unicode, идентичную последовательности, заданной структурой CONSTANT_String_info, тогда результат строкового литерала является ссылкой на тот же экземпляр class String.
- В противном случае создается новый экземпляр класса String, содержащий последовательность кодовых точек Unicode, заданную структурой CONSTANT_String_info; ссылка на этот экземпляр класса является результатом строкового литерала. Наконец, вызывается метод intern нового экземпляра String.
Bytecode
Также полезно посмотреть на реализацию байт-кода на OpenJDK 7.
Если мы декомпилируем:
public class StringPool { public static void main(String[] args) { String a = "abc"; String b = "abc"; String c = new String("abc"); System.out.println(a); System.out.println(b); System.out.println(a == c); } }
, мы имеем в пуле констант:
#2 = String #32 // abc [...] #32 = Utf8 abc
и
main
:0: ldc #2 // String abc 2: astore_1 3: ldc #2 // String abc 5: astore_2 6: new #3 // class java/lang/String 9: dup 10: ldc #2 // String abc 12: invokespecial #4 // Method java/lang/String."
":(Ljava/lang/String;)V 15: astore_3 16: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 19: aload_1 20: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 23: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 30: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 33: aload_1 34: aload_3 35: if_acmpne 42 38: iconst_1 39: goto 43 42: iconst_0 43: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V Обратите внимание:
0
и3
: загружена константаldc #2
(литералы)12
: новая строка экземпляр создается с помощью#2
в качестве аргумента)35
:a
иc
сравниваются как обычные объекты сif_acmpne
представление постоянных строк довольно магия на байт-коде:
- у него есть специальная структура CONSTANT_String_info , в отличие от обычных объектов (например,
new String
)- struct указывает на CONSTANT_Utf8_info Structure , которая содержит данные. Это единственные необходимые данные для представления строки.
и приведенная выше цитата JVMS говорят, что всякий раз, когда указатель Utf8 одинаковый, то идентичные экземпляры загружаются с помощью
ldc
.Я сделал аналогичные тесты для полей и:
static final String s = "abc"
указывает на таблицу констант через ConstantValue Attribute- не конечные поля не имеют этого атрибута, но все еще могут быть инициализированы с помощью
ldc
. Заключение: имеется прямая поддержка байт-кода для пула строк и представление памяти
Бонус: сравните это с пулом Integer , который не имеет поддержки прямого байт-кода (т. е. аналога
CONSTANT_String_info
).