Урок 3 из 11 старт около 25 мин
Текстовые блоки и современные методы строк
Многострочные литералы без экранирования, formatted(), strip(), lines(), repeat() и приёмы, которые заменяют громоздкую конкатенацию.
Проблема: многострочный текст в коде
До Java 15 любой фрагмент JSON, SQL или HTML внутри программы превращался в лестницу из кавычек, плюсов и \n. Такой код тяжело читать и легко сломать, забыв пробел на стыке строк. Текстовые блоки решают именно эту задачу: строковый литерал может занимать несколько строк и не требует экранирования кавычек внутри.
// Было
String json = "{\n" +
" \"name\": \"Фортуна\",\n" +
" \"city\": \"Москва\"\n" +
"}";
// Стало
String json = """
{
"name": "Фортуна",
"city": "Москва"
}
""";
Блок начинается с трёх кавычек и обязательного перевода строки, а заканчивается тремя кавычками. Результат — обычная строка String: у неё нет специального типа, и её можно передавать куда угодно.
Как компилятор убирает отступы
Самый частый вопрос: куда деваются пробелы слева. Компилятор находит наименьший общий отступ среди всех непустых строк блока и закрывающих кавычек и удаляет ровно его. Поэтому положение закрывающих """ управляет отступом всего текста:
String a = """
первая
вторая
"""; // "первая\n вторая\n"
String b = """
первая
вторая
"""; // " первая\n вторая\n"
Если закрывающие кавычки стоят на той же строке, что и последний текст, завершающего перевода строки в результате не будет. Пробелы в конце строк компилятор отбрасывает; чтобы сохранить их намеренно, используйте escape-последовательность \s.
Две новые escape-последовательности
\s— один пробел, который переживёт удаление хвостовых пробелов;\в самом конце строки — соединить её со следующей без перевода строки. Так удобно переносить длинное предложение в коде, не добавляя\nв результат.
String message = """
Это длинное предложение, которое в коде удобнее \
разбить на две строки, но в результате оно останется одним.
""";
Подстановка значений: formatted и String.format
Текстовые блоки не умеют вставлять переменные сами. Для этого у строки есть метод formatted, который работает как String.format, но вызывается на самом шаблоне:
String letter = """
Здравствуйте, %s!
Ваш урок номер %d начинается в %s.
""".formatted("Анна", 3, "19:00");
System.out.print(letter);
Плейсхолдер %s принимает любое значение, %d — целое, %.2f — число с двумя знаками после запятой. Для дат лучше использовать форматирование из java.time, о котором речь в девятом уроке, а не подстановку через %s.
Методы строк, которых не было в старых учебниках
| Метод | Что делает | Пример |
|---|---|---|
strip() | Убирает пробелы и любые Unicode-пробельные символы по краям; trim() знал только про ASCII. | " текст ".strip() → "текст" |
isBlank() | Истина, если строка пуста или состоит из пробелов. | " ".isBlank() → true |
lines() | Поток строк, разбитый по переводам строки любой платформы. | text.lines().count() |
repeat(n) | Повторяет строку n раз. | "-".repeat(20) |
indent(n) | Сдвигает каждую строку на n пробелов; отрицательное n убирает отступ. | code.indent(4) |
chars() | Поток кодов символов для подсчётов и фильтрации. | s.chars().filter(Character::isDigit).count() |
String.join | Склеивает элементы через разделитель. | String.join(", ", list) |
Пример: отчёт в консоль
Соберём таблицу без сторонних библиотек. Здесь пригодятся и текстовый блок, и repeat, и форматирование ширины колонок через %-12s:
record Row(String lesson, int minutes) {}
var rows = java.util.List.of(new Row("JShell", 25), new Row("var", 20), new Row("Строки", 25));
var header = """
%-12s %7s
%s
""".formatted("Урок", "Минуты", "-".repeat(20));
var body = new StringBuilder(header);
for (var row : rows) {
body.append("%-12s %7d%n".formatted(row.lesson(), row.minutes()));
}
System.out.print(body);
Урок Минуты
--------------------
JShell 25
var 20
Строки 25
Спецификатор %n вставляет перевод строки, правильный для текущей операционной системы. Записи record из примера подробно разобраны в следующем уроке.
Частая ошибка
Склеивать строки плюсом в цикле. Каждая конкатенация создаёт новый объект, и на тысячах итераций это заметно. Для накопления текста используйте StringBuilder или Collectors.joining, а плюс оставьте для двух-трёх слагаемых.
Попробуйте сами
- Запишите текстовым блоком шаблон письма с двумя подстановками: имя и дата. Выведите его для двух разных получателей через
formatted. - Возьмите многострочную строку и посчитайте через
lines(), сколько в ней непустых строк. Подсказка:filter(line -> !line.isBlank()). - Поэкспериментируйте с положением закрывающих кавычек текстового блока и объясните, как изменился отступ результата. Проверьте вывод через
System.out.print, а неprintln, чтобы увидеть, есть ли перевод строки в конце.