Чому клас 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]]