W3docs

Форматирование строк в Java

Форматируйте строки в Java с помощью String.format и printf, используя спецификаторы %s, %d, %f, %n.

Конкатенация (+) подходит для коротких и простых строк. Но как только нужно зафиксировать число с определённым количеством знаков после запятой, выровнять столбцы отступами, вставить дату в стандартном формате или иначе форматировать значения вместо простой вставки — приходит время воспользоваться API в стиле printf, который Java позаимствовала из C.

Существует три тесно связанных точки входа, и все они встречаются в реальном коде:

  • String.format(fmt, args...) — возвращает отформатированную String.
  • "...".formatted(args...) — экземплярная форма, добавленная в Java 15. Результат идентичен, удобнее для цепочек вызовов.
  • System.out.printf(fmt, args...) — печатает напрямую в PrintStream (или любой Formatter).

Все три используют один и тот же синтаксис спецификаторов формата. Изучите его один раз.

Спецификаторы формата

Спецификатор имеет вид %[flags][width][.precision]conversion. Буква преобразования — единственный обязательный элемент. Остальное настраивает ширину, выравнивание, заполнение и точность.

String s = String.format("%-10s | %5d | %8.2f", "apples", 42, 3.14159);
// "apples     |    42 |     3.14"

Наиболее полезные преобразования:

ПреобразованиеАргументЗначение
%sлюбойtoString() значения
%dцелоедесятичное целое число
%fс плавающей точкойчисло с фиксированной точкой
%eс плавающей точкойнаучная запись
%gс плавающей точкойкратчайшее из %e и %f
%x, %oцелоешестнадцатеричное / восьмеричное
%cсимволодиночный символ
%bлюбойtrue/false (null → "false")
%nнетразделитель строк для текущей платформы
%%нетлитеральный символ %
%t...дата/времяцелое семейство — %tF, %tT и др.

Ширина дополняет вывод минимум до N символов; точность означает «цифры после запятой» для чисел с плавающей точкой и «максимальное число символов» для строк.

Флаги: выравнивание, знак, нули, группировка

Несколько флагов размещаются между % и шириной:

  • - — выравнивание по левому краю в пределах ширины. По умолчанию — по правому краю.
  • 0 — дополнять нулями до заданной ширины (только для числовых преобразований).
  • + — всегда показывать знак у чисел (+42, -7).
  • (пробел) — показывать ведущий пробел для положительных чисел, как +, но с пробелом.
  • , — группировать цифры с помощью разделителя тысяч, принятого в локали.
  • ( — отрицательные числа в скобках, бухгалтерский стиль.
String.format("%08d", 42);        // "00000042"
String.format("%,d", 1234567);    // "1,234,567"
String.format("%+.2f", 3.14159);  // "+3.14"
String.format("%-10s|", "hi");    // "hi        |"

Ширина и точность

Ширина — это минимальная ширина поля: если отформатированное значение шире, ничего не усекается.

Точность означает разное для разных преобразований:

  • %.3f — три цифры после десятичной точки.
  • %.10s — усечь строку до не более 10 символов.
  • %.4e — четыре цифры точности мантиссы.

Комбинирование ширины и точности часто используется, когда нужно одновременно выровнять столбцы и округлить числа:

String.format("%10.4f", Math.PI);   // "    3.1416"
String.format("%-10.4s", "abcdef"); // "abcd      "

%n против \n

%n выводит разделитель строк, соответствующий платформе: "\n" на Unix, "\r\n" на Windows. \n — всегда ровно один байт. Для файлов и протокольного вывода, где конец строки важен точно, лучше использовать \n и выбирать его осознанно. Для консольного вывода, который должен корректно отображаться на любой ОС, где запущена JVM, %n — более безопасный выбор.

Индексы аргументов: повторное использование и переупорядочение

Спецификатор вида %N$... ссылается на N-й аргумент (нумерация с 1). Полезен, когда одно значение встречается в шаблоне более одного раза или когда естественный порядок чтения отличается от порядка аргументов:

String.format("%1$s, %1$s, %1$s!", "go");        // "go, go, go!"
String.format("%2$s before %1$s", "lunch", "tea"); // "tea before lunch"

Это правильный инструмент при локализации шаблонов — в разных языках существительные стоят на разных позициях, и переводчик может переставлять плейсхолдеры, не трогая место вызова.

Локаль важна для чисел и дат

Форматирование чисел учитывает локаль JVM по умолчанию, если вы не переопределите её явно. В en-US вы получите 3.14; в de-DE3,14; во fr-FR тысячи разделяются неразрывным пробелом. Для вывода, предназначенного человеку, это обычно то, что нужно. Для форматов данных — JSON, CSV, лог-файлов, любого машиночитаемого формата — это катастрофа, ожидающая развёртывания во Франкфурте.

Всегда явно передавайте локаль для машиночитаемого вывода:

String json = String.format(Locale.ROOT, "{\"price\": %.2f}", 19.95);
// "{\"price\": 19.95}" — always, regardless of JVM locale

Locale.ROOT означает «без форматирования, специфичного для локали» — точка как десятичный разделитель, без группировки. Locale.US — другой распространённый вариант для той же цели. Опасность — не передавать локаль и делать предположения.

Форматирование даты и времени

%t — мета-преобразование: следующая за ним буква выбирает поле. Один и тот же аргумент типа Date, Calendar, Long (миллисекунды) или java.time.temporal.TemporalAccessor можно форматировать множеством способов:

LocalDateTime now = LocalDateTime.of(2026, 5, 29, 14, 30, 15);
String.format("%tF",   now);          // "2026-05-29"  — ISO date
String.format("%tT",   now);          // "14:30:15"    — 24-hour time
String.format("%tA",   now);          // "Friday"      — locale-dependent
String.format("%1$tF %1$tT", now);    // "2026-05-29 14:30:15"

Для всего, что выходит за рамки быстрого форматирования, API java.time.format.DateTimeFormatter более гибкий и учитывает локаль — но %tF и подобные им по-прежнему удобны в строках лога.

Типичные ошибки

  • Неверное преобразование для типа. String.format("%d", 3.14) выбрасывает IllegalFormatConversionException во время выполнения — %d ожидает целое, %f ожидает число с плавающей точкой. Компилятор это не проверяет.
  • Отсутствующие аргументы. Пропуск аргумента плейсхолдера выбрасывает MissingFormatArgumentException.
  • Зависящий от локали десятичный разделитель в машинном выводе. Рассмотрено выше.
  • %s для null. Выдаёт "null". Допустимо для логов, неудобно в пользовательском интерфейсе.
  • Использование + для отформатированных чисел. "Price: " + 19.95 даёт "Price: 19.95", но "Price: " + 0.1 + 0.2 даёт "Price: 0.10.2", а не "Price: 0.3" — это конкатенация, а не сложение.

Практический пример

Небольшой форматировщик сводки заказа, демонстрирующий ширину, точность, выравнивание, группировку, локаль и преобразование дат через %t. Результат — аккуратный двухколоночный отчёт, который иначе пришлось бы строить вручную с помощью вспомогательных функций padLeft.

java— editable, runs on the server

Два момента, заслуживающих внимания: %<tT в строке «Placed» повторно использует предыдущий аргумент (< — флаг обратной ссылки), избегая лишнего второго placedAt. А строка JSON использует Locale.ROOT — тот же код на немецкой JVM по-прежнему выдаёт 1339.43, а не 1339,43, что именно и ожидает JSON-парсер.

Что дальше

Построение строк — это половина задачи. Вторая половина — их сравнение: равенство, упорядочение, приведение регистра, разница между == и equals — и это частый источник неочевидных ошибок. Переходите к сравнению строк в Java.

Практика

Практика
Вы хотите отформатировать `double` total как строку с разделителем тысяч и ровно двумя знаками после запятой, причём десятичным разделителем должна быть точка — независимо от локали JVM (чтобы JSON-парсер мог разобрать значение). Какой вызов делает это правильно?
Вы хотите отформатировать `double` total как строку с разделителем тысяч и ровно двумя знаками после запятой, причём десятичным разделителем должна быть точка — независимо от локали JVM (чтобы JSON-парсер мог разобрать значение). Какой вызов делает это правильно?
Was this page helpful?