Короткий ответ
Все три класса работают со строками, но различаются неизменяемостью и потокобезопасностью. String неизменяем: любой «изменяющий» метод создаёт новую строку. StringBuilder и StringBuffer изменяемы: они модифицируют внутренний буфер через append, insert, replace, не создавая новые объекты. Методы StringBuffer объявлены synchronized, поэтому он потокобезопасен, но медленнее StringBuilder.
Как это работает подробнее
StringBuffer существует с Java 1.0, его методы синхронизированы; StringBuilder появился в Java 5 как несинхронизированный аналог. Вне циклов компилятор сам превращает конкатенацию через + в вызовы StringBuilder, но внутри цикла это не спасает от множества промежуточных объектов.
- String — неизменяемый, подходит для строк, которые не меняются; переиспользуется в пуле.
- StringBuilder — изменяемый, без синхронизации, самый быстрый в однопоточном коде; появился в Java 5.
- StringBuffer — изменяемый, методы synchronized, потокобезопасный, но медленнее; существует с Java 1.0.
- Компилятор — простая конкатенация через + вне цикла собирается им в StringBuilder.
Пример кода
ConcatDemo.java
public class ConcatDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // модифицируется тот же объект
}
String result = sb.toString();
}
}Что использовать на практике
- Строка не меняется — используй String.
- Частые модификации в одном потоке — StringBuilder: он быстрее StringBuffer.
- StringBuffer бери только когда одним буфером действительно пользуются несколько потоков.
Подводные камни
- Конкатенация через + в цикле создаёт множество временных объектов — заменяй её на StringBuilder.append().
- Потокобезопасность StringBuffer стоит дорого: синхронизация замедляет даже однопоточный код.
- Не забывай превращать результат StringBuilder в String через toString().
Как отвечать на собеседовании
- Сопоставь три класса по осям неизменяемость и потокобезопасность → отметь историю (StringBuffer с Java 1.0, StringBuilder с Java 5) → приведи цикл с sb.append(...) → добавь, что компилятор сам собирает конкатенацию вне циклов → сформулируй правило выбора.