Когда использовать intern() для строковых литералов
Я вижу много устаревшего кода, подобного этому:
class A {
public static final String CONSTANT = "value".intern();
...
}
Я не вижу никакой причины для intern(), так как в Javadoc можно прочитать: "Все литеральные строки и строковые константные выражения интернированы". Есть ли какое-то намерение в этом, может быть, в прошлых версиях языка?
4 ответа
Это метод для обеспечения того, чтобы CONSTANT
на самом деле не является константой.
Когда компилятор Java видит ссылку на конечный статический примитив или String, он вставляет фактическое значение этой константы в класс, который ее использует. Если затем вы измените значение константы в определяющем классе, но не перекомпилируете класс using, он продолжит использовать старое значение.
Вызывая intern() для "постоянной" строки, он больше не считается статической константой компилятором, поэтому класс using будет фактически обращаться к члену определяющего класса при каждом использовании.
JLS цитаты:
определение постоянной времени компиляции: http://docs.oracle.com/javase/specs/jls/se6/html/expressions.html
последствия изменения константы времени компиляции (примерно на полпути вниз по странице): http://docs.oracle.com/javase/specs/jls/se6/html/binaryComp.html
Использование intern()
с константой строковой литерал является пустой тратой времени, так как литерал уже будет интернирован, как указано в разделе kdgregory. kdgregory спецификации языка Java®.
Цитирование из Java SE 8 Edition:
Более того, строковый литерал всегда ссылается на один и тот же экземпляр класса String. Это связано с тем, что строковые литералы - или, в более общем случае, строки, являющиеся значениями константных выражений (§15.28) - "интернированы", чтобы обмениваться уникальными экземплярами, используя метод String.intern.
Я думаю, кодер не оценил этот факт.
Редактировать:
Как указала kdgregory, это влияет на то, как эта константа может быть встроена.
kdgregory - https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html
Некоторое время назад я intern () редактировал все строки, поступающие из файлов классов (для анализатора файлов классов). Intern() заставил программу использовать меньше памяти (в данном случае не будет, как указывали другие), но значительно замедлил работу программы (я думаю, что для анализа всего файла rt.jar потребовалось 4 секунды, и это изменение положило его более 8 секунд). Рассматривая его в то время (я думаю, что это был JDK 1.4), код intern () выглядит довольно уродливо и медленнее, чем, вероятно, должно быть.
Если бы я подумал о вызове intern () в своем коде, я сначала профилировал бы его без intern (), а затем профилировал его с помощью intern () для памяти и скорости и увидел, какой из них "хуже" для изменения.
Я использовал intern() для "блокировки". Например, допустим, у меня есть "хранилище" "торговых записей". Пока я редактирую и обновляю сделку, я хочу заблокировать сделку; Вместо этого я мог бы заблокировать tradeId.intern(), чтобы мне не пришлось беспокоиться о клонах сделки, плавающей вокруг. Я не уверен, всем ли нравится это использование.
Это предполагает, что поле id вряд ли случайно столкнется с полем id другого объекта домена - случайный TradeId не сталкивается, например, с account_number, где можно также
synchronized(account.getAccountNumber().intern()) {...}
смотри пример