Java-интернирование, что гарантировано?

Вопрос сводится к этому коду:

// setup
String str1 = "some string";
String str2 = new String(str1);
assert str1.equals(str2);
assert str1 != str2;
String str3 = str2.intern();

// question cases
boolean case1 = str1 == "some string";
boolean case2 = str1 == str3;

Предоставляет ли Java-стандарт какие-либо гарантии относительно значений case1 и case2? Конечно, ссылка на соответствующую часть спецификации Java будет приятной.

Да, я посмотрел на все "похожие вопросы", найденные SO, и не нашел дубликатов, так как я не нашел ответа на этот вопрос. И нет, дело не в ошибочной идее "оптимизации" сравнения строк, заменив equals на ==.

Ответ 1

Вот ваша цитата JLS, Раздел 3.10.5:

Каждый строковый литерал является ссылкой (§4.3) экземпляру (§4.3.1, § 12.5) класса String (п. 4.3.3). Объекты String имеют постоянное значение. Строковые литералы или, в более общем смысле, строки, которые являются значениями постоянные выражения (§15.28) - "интернированы", чтобы делиться уникальными экземпляров, используя метод String.intern.

Таким образом, тестовая программа, состоящая из единицы компиляции (§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

Этот пример иллюстрирует шесть пунктов:

Литеральные строки в одном классе (§8) в том же пакете (§7) представляют ссылки на один и тот же объект String (§4.3.1).

Литеральные строки в разных классах в одном пакете представляют ссылки на один и тот же объект String.

Литеральные строки в разных классах в разных пакетах аналогично представляют ссылки на один и тот же объект String.

Строки, вычисленные постоянными выражениями (§15.28), вычисляются в компилировать время, а затем обрабатывать, как если бы они были литералами.

Строки, вычисленные путем конкатенации во время выполнения, создаются и поэтому различный. Результат явного интернирования вычисленного строка является той же строкой, что и любая ранее существовавшая литеральная строка с такое же содержание.

В сочетании с JavaDoc для стажера, и у вас достаточно информации, чтобы вывести, что оба ваших случая вернут true.

Ответ 2

Я думаю, что API String.intern предоставляет достаточно информации

Пул строк, первоначально пустой, поддерживается в частном порядке классом String.

Когда вызывается метод intern, если пул уже содержит строку, равную этому объекту String, как определено методом equals (Object), возвращается строка из пула. В противном случае этот объект String добавляется в пул и возвращается ссылка на этот объект String.

Из этого следует, что для любых двух строк s и t s.intern() == t.intern() истинно тогда и только тогда, когда s.equals(t) истинно.

Все литералы и строковые константные выражения интернированы. Строковые литералы определены в разделе 3.10.5 Спецификации языка Java ™.