Строки в Java
String — это неизменяемая последовательность символов. Слово «неизменяемая» здесь не деталь, а фундамент: содержимое строки фиксируется при создании и не меняется никогда, а любой метод, который выглядит как изменение (toUpperCase, trim, replace, concat), возвращает новый объект, оставляя исходный нетронутым. Из этого одного свойства вырастает почти всё, что спрашивают про строки.
Неизменяемость делает безопасным разделение одного объекта между многими держателями — на этом построен пул строк, где одинаковые литералы делят единственный объект, а new String(...) намеренно создаёт отдельный. Она же делает строку надёжным ключом хэш-таблицы и потокобезопасной без синхронизации. А когда строку действительно нужно собирать по кускам, на сцену выходят изменяемые построители — StringBuilder и StringBuffer, — потому что конкатенация неизменяемых строк в цикле стоит O(n²). Полная карта — в слоях ниже.
Карта темы
- Неизменяемость String — содержимое не меняется, «изменяющие» методы возвращают новый объект, и что это даёт.
- Пул строк — общий кэш литералов,
==противequals,new String()иintern(). - StringBuilder и StringBuffer — изменяемые построители, почему
+в цикле — O(n²) и кто из них синхронизирован.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Считать, что replace/trim/concat меняют строку на месте | Исходная строка не меняется; результат теряется, если его не присвоить |
Сравнивать строки через == | Сравниваются ссылки, а не значения — new String("x") == "x" ложно |
Считать, что new String("x") и литерал "x" — один объект | Это разные объекты; == ложно, equals истинно |
Думать, что intern() происходит автоматически для каждой строки | В пуле по умолчанию только литералы; динамические строки нужно интернировать явно |
Строить текст конкатенацией + в цикле | Каждая итерация создаёт новый String и копирует всё — O(n²) вместо O(n) |
Считать StringBuilder потокобезопасным | Он не синхронизирован; общий экземпляр между потоками портится гонкой |
Значение для собеседований
Строки спрашивают почти на каждом Java-интервью, но проверяют не знание методов, а следствия неизменяемости: почему сравнивают через equals, зачем нужен пул и когда брать построитель вместо +.
Что обычно проверяют:
- Что
Stringнеизменяем и что именно это даёт (пул, ключи, потокобезопасность). - Как работает пул строк и чем литерал отличается от
new String(). - Что делает
intern(). - Почему
+в цикле — O(n²) и как это чинитStringBuilder. - Разницу
StringBuilderиStringBufferпо синхронизации.
Типичный неверный ответ: «строки надо сравнивать через ==, если они равны». Это открывает разговор о том, что == проверяет идентичность объекта, а не равенство значений: два равных литерала совпадут из-за пула, но new String("x") даст false — поэтому по значению строки всегда сравнивают через equals.