Почему класс String является иммутабельным?
Класс String в Java нельзя изменить после создания. Любая операция, которая "меняет" строку, на самом деле создаёт новую строку.
Junior Level
Класс String в Java нельзя изменить после создания. Любая операция, которая “меняет” строку, на самом деле создаёт новую строку.
String s = "Hello";
s.concat(" World"); // s всё ещё "Hello", результат нужно присвоить
s = s.concat(" World"); // теперь s = "Hello World" (новый объект)
Почему так сделали?
- Безопасность — строки используются для путей к файлам, URL, подключений к БД
- Потокобезопасность — строки можно передавать между потоками без проблем
- Экономия памяти — одинаковые строки переиспользуются (String Pool)
Обратная сторона: хранение паролей в String — плохая практика (см. файл 5).
Middle Level
String Pool
При создании строки-литерала JVM проверяет, есть ли уже такая строка в пуле. Если есть — возвращает ссылку на неё, если нет — создаёт новую:
String a = "Hello";
String b = "Hello";
System.out.println(a == b); // true — одна и та же ссылка из пула
Безопасность
Строки используются как параметры при открытии файлов, сетевых соединений, подключении к БД. Если бы строки были изменяемыми, злоумышленник мог бы передать валидный путь, дождаться проверки, а затем изменить его в памяти.
Стабильность hashCode
String кэширует свой hashCode() при первом вычислении. Это делает строки идеальными ключами для HashMap:
Map<String, Integer> map = new HashMap<>();
map.put("key", 42); // hashCode вычислен один раз
map.get("key"); // используется тот же hashCode
Как это реализовано в JDK
- Класс помечен как
final(запрет наследования) - Внутреннее поле
byte[] value(Java 9+) илиchar[](старые версии) —private final - Нет методов, модифицирующих это поле
Senior Level
Архитектурное значение
Иммутабельность String — это не просто удобство, а фундаментальный architectural decision, на котором базируются:
- String Pool (Flyweight паттерн) — без иммутабельности пул невозможен: изменение строки в одном месте сломает все ссылки на неё
- Security Manager — проверки доступа к файлам, сетям, классам основаны на неизменяемости строк-параметров
- Class Loading — имена классов и путей загрузки неизменяемы, что предотвращает подмену классов
- HashMap стабильность — кэшированный
hashCodeгарантирует корректную работу хеш-таблиц
Эволюция реализации
- Java 8 и раньше:
private final char[] value— каждый символ 2 байта (UTF-16) - Java 9+:
private final byte[] value+ полеcoder— Compact Strings (Latin-1 = 1 байт, UTF-16 = 2 байта) // Замена char[] на byte[] (Java 9+, Compact Strings) экономит ~50% памяти для строк с латинскими символами.
Резюме для Senior
- Иммутабельность
String— фундамент для String Pool, Security и Thread Safety - Позволяет JVM оптимизировать работу с памятью и кэшировать хеш-коды
- Любое “изменение” строки порождает новый объект в куче
- Без иммутабельности
Stringвся платформа Java была бы уязвимой. Пример TOCTOU-атаки: проверяется путь/safe/path, но затем строка меняется на/etc/passwd.
🎯 Шпаргалка для интервью
Обязательно знать:
- String — final класс, приватное поле
byte[] value, нет методов модификации - String Pool — одинаковые строки переиспользуются, работает ТОЛЬКО благодаря иммутабельности
- Безопасность — строки используются для путей к файлам, URL, подключений к БД
- Стабильность hashCode — String кэширует hashCode, идеален как ключ HashMap
- Эволюция: Java 8
char[](2 байта/символ) → Java 9+byte[]+ coder (Compact Strings, 1 байт для Latin-1) - Без иммутабельности String вся платформа Java уязвима (TOCTOU-атаки)
Частые уточняющие вопросы:
- Почему String final? — Чтобы подкласс не добавил мутабельность и не переопределил hashCode/equals
- Что такое Compact Strings? — Java 9+: латинские символы хранятся в 1 байте вместо 2
- Почему String кэширует hashCode? — Вычисляется один раз, ускоряет работу в HashMap
- Что было бы без иммутабельности String? — String Pool невозможен, Security Manager обойдён
Красные флаги (НЕ говорить):
- «String можно менять через concat» — concat создаёт НОВУЮ строку, оригинал не меняется
- «String Pool в PermGen» — с Java 7+ пул переехал в Heap
- «Иммутабельность String — только удобство» — это фундаментальный architectural decision
- «byte[] значит String mutable» — поле
private final, доступа извне нет
Связанные темы:
- [[5. Какие последствия иммутабельности String]]
- [[18. Как работает String pool и как это связано с иммутабельностью]]
- [[19. Можно ли изменить значение String через рефлексию]]
- [[27. Можно ли использовать иммутабельные объекты как ключи в HashMap]]