Когда использовать intern() для строковых литералов

Я вижу много устаревшего кода:

class A {
    public static final String CONSTANT = "value".intern();
    ...
}

Я не вижу причин для intern(), как в Javadoc можно прочитать: "Все литералы и строковые константные выражения интернированы". Есть ли какие-то намерения этого, может быть, в прошлых версиях языка?

Ответ 1

Это метод, гарантирующий, что CONSTANT фактически не является константой.

Когда компилятор Java видит ссылку на конечный статический примитив или String, он вставляет фактическое значение этой константы в класс, который ее использует. Если вы затем измените значение константы в определяющем классе, но не перекомпилируете класс using, он будет продолжать использовать старое значение.

Вызывая intern() в строке "константа", он больше не считается статической константой компилятором, поэтому используемый класс будет фактически обращаться к члену определяющего класса при каждом использовании.


JLS цитаты:

Ответ 2

Использование intern() с константным строковым литералом - пустая трата времени, поскольку литерал уже интернирован, как указано в разделе 3.10.5. Строковые литералы Спецификация языка Java®.

Цитата из Java SE 8 Edition:

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

Я думаю, кодер не оценил этот факт.

Edit:

Как kdgregory указал, что есть влияние на то, как эта константа может быть встроена.

1 - https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5

Ответ 3

Некоторое время назад я intern() редактировал все строки, поступающие из файлов классов (для парсера classfile). Intern() ing заставлял программу использовать меньше памяти (в этом случае не будет указано, как это указывали другие), но она значительно замедляла программу (я думаю, что потребовалось 4 секунды, чтобы разобрать все rt.jar и это изменить более 8 секунд). Заглянув в нее в то время (был JDK 1.4, я думаю), код intern() довольно уродливый и медленный, что, вероятно, должно быть.

Если бы я подумал о вызове intern() в моем коде, я бы сначала профилировал его без intern(), а затем профилировал его с помощью intern() для памяти и скорости и посмотрел, какой из них "хуже" для изменения.

Ответ 4

Я использовал intern() для "блокировки". Например, скажем, у меня есть "хранилище" "торговых записей". Пока я редактирую и обновляю сделку, я хочу заблокировать сделку; Вместо этого я могу заблокировать tradeId.intern(), так что мне не нужно беспокоиться о клонах торговли, плавающей вокруг. Я не уверен, что всем нравится это использование.

Это предполагает, что поле id вряд ли случайно столкнется с полем идентификатора другого объекта домена - например, tradeId не сталкивается с именем account_number, где также можно было бы делать

synchronized(account.getAccountNumber().intern()) {...}

см. пример