Почему строки (String) в Java являются неизменяемыми (immutable)?

«Почему строки (String) в Java являются неизменяемыми (immutable)?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Основные причины:

  1. Безопасность (Security): Строки часто хранят критичные данные (пароли, токены, пути к файлам). Если бы строка была изменяемой, переданная ссылка могла быть модифицирована получателем, что создает уязвимости.
  2. Потокобезопасность (Thread Safety): Неизменяемые объекты по своей природе потокобезопасны. Их можно свободно передавать между потоками без риска состояния гонки (race condition) и без необходимости синхронизации.
  3. Кэширование хэш-кода: Класс String кэширует значение хэш-кода (hashCode) при первом вычислении. Поскольку строка не меняется, это значение гарантированно остается корректным, что резко ускоряет работу в HashMap, HashSet и других структурах, основанных на хэшах.
  4. Оптимизация памяти (String Pool): JVM использует пул строк (String Pool) для хранения литералов. Если несколько переменных ссылаются на строку с одинаковым значением "Hello", они указывают на один объект в пуле. Изменяемость разрушила бы эту оптимизацию.

Пример и демонстрация:

String s1 = "Hello"; // Помещается в String Pool
String s2 = "Hello"; // s2 получает ссылку на тот же объект из пула, что и s1
System.out.println(s1 == s2); // true (ссылки равны)

s1 = s1.concat(" World"); // Не изменяет исходный "Hello", а создает НОВУЮ строку "Hello World"
System.out.println(s1); // "Hello World"
System.out.println(s2); // "Hello" (оригинальная строка не тронута)

Для работы с изменяемыми последовательно символов следует использовать StringBuilder или StringBuffer.