Можно ли изменять строки в Java
Что проверяют
Знаете ли вы, что String неизменяемый, и понимаете ли последствия: пул строк, потокобезопасность, поведение конкатенации в цикле. Следом обычно спрашивают, чем StringBuilder отличается от StringBuffer и что происходит при + внутри цикла.
Как отвечать
Ответ прямой: нет, содержимое существующего объекта String изменить нельзя. Внутри лежит приватный финальный массив (начиная с JDK 9 это byte[] плюс поле coder, компактные строки хранят латиницу по байту на символ), наружу он не отдаётся. Любой concat, replace, substring, toUpperCase возвращает новый объект, а исходный остаётся прежним. Классическая ловушка: s.replace("a", "b"); без присваивания результата не делает ничего.
Зачем так сделано:
- пул строк: литералы лежат в общем пуле, одинаковые литералы это один объект, и изменяемость сломала бы всех владельцев ссылки;
- ключи в
HashMap:hashCodeсчитается один раз и кешируется в поле, у изменяемого ключа корзина уплывёт и значение станет недостижимым; - потоки: неизменяемый объект отдаётся в другие потоки без синхронизации;
- безопасность: имена классов, пути и параметры соединения нельзя подменить между проверкой и использованием.
Как менять на практике: StringBuilder для обычного кода и StringBuffer с синхронизированными методами, нужный крайне редко. Деталь про производительность: + в одном выражении компилятор с JDK 9 собирает через invokedynamic и StringConcatFactory, это быстро и ручной StringBuilder там не нужен. А вот + в цикле создаёт новый объект на каждой итерации, и вот там берут StringBuilder, String.join или Collectors.joining.
Где спотыкаются
Говорят, что строка неизменяема, потому что переменная объявлена как final. Это разное: final у ссылки запрещает переприсваивание переменной, а неизменяемость даёт закрытое поле массива и отсутствие сеттеров.
Путают == и equals. Два одинаковых литерала совпадут по ссылке из-за пула, а new String("a") создаст отдельный объект, и == вернёт false.
Утверждают, что StringBuffer безопаснее и брать надо его. Синхронизация каждого append стоит времени, а от гонки на уровне логики не спасает.