Форматирование строк в Java: String.format(), printf() и Formatter - Вопросы

Всего: 5 вопросов

1. 

Чем отличаются String.format(), formatted(), System.out.printf() и класс Formatter в Java?

Все они понимают один и тот же синтаксис шаблона и внутри используют java.util.Formatter. Различаются тем, куда идёт результат:

  • String.format(шаблон, args...) (Java 5) возвращает новую строку — аналог sprintf из C;
  • "шаблон".formatted(args...) (Java 15) делает то же, но короче, удобен с текстовыми блоками; перегрузки с Locale у него нет — всегда используется локаль по умолчанию;
  • System.out.printf() и System.out.format() — два имени одного метода PrintStream, печатают сразу в консоль; перевод строки сами не добавляют, его пишут в шаблоне как %n;
  • Formatter накапливает текст из нескольких вызовов format() и может писать в StringBuilder, файл или поток.
String a = String.format("%s: %d", "Анна", 5);
String b = "%s: %d".formatted("Анна", 5);
System.out.printf("%s: %d%n", "Анна", 5);

StringBuilder sb = new StringBuilder();
Formatter f = new Formatter(sb, Locale.US);
f.format("%-8s%6.2f%n", "Чай", 3.5);
2. 

Из каких частей состоит спецификатор формата в Java и какие из них обязательны?

Общий вид: %[argument_index$][flags][width][.precision]conversion. Обязательны только знак % и символ преобразования (d, s, f, x…), остальное необязательно:

  • argument_index$ — номер аргумента с 1 (%2$s); %<s берёт тот же аргумент, что предыдущий спецификатор;
  • flags — выравнивание, знак, ведущие нули, разделители групп (-, +, 0, ,, (, #, пробел);
  • width — минимальная ширина поля: короткое значение дополняется пробелами, длинное выводится целиком;
  • .precision — для дробных число знаков после точки с округлением HALF_UP, для строк — максимальная длина.
String.format(Locale.US, "[%8.2f]", 1000.0 / 3.0); // [  333.33]
String.format("[%.3s]", "Formatter");               // [For]
String.format("[%3s]", "Formatter");                // [Formatter]

Заглавные варианты (%S, %X, %B…) переводят результат в верхний регистр; для %d, %f, %o заглавных вариантов нет.

3. 

Как отформатировать дату через String.format() и почему шаблон "%td.%tm.%tY" с одной датой падает?

Для дат используется спецификатор %t (или %T) с суффиксом: %tY — год, %tm — месяц, %td — день, %tF — дата в формате ISO. Аргументом может быть LocalDate, LocalDateTime, Date, Calendar или long.

Каждый спецификатор без индекса берёт следующий аргумент, поэтому "%td.%tm.%tY" с одной датой ждёт три аргумента и бросает MissingFormatArgumentException. Нужно сослаться на один аргумент через индекс %1$:

LocalDate date = LocalDate.of(2026, 10, 16);
String.format("%tF", date);                 // 2026-10-16
String.format("%1$td.%1$tm.%1$tY", date);   // 16.10.2026
String.format("%td.%tm.%tY", date);         // MissingFormatArgumentException

Для сложных форматов дат в современном коде удобнее java.time.format.DateTimeFormatter.

4. 

Какие ошибки в шаблоне String.format() встречаются чаще всего и какие исключения они вызывают?

Компилятор шаблон не проверяет: ошибки проявляются только при выполнении в виде непроверяемых исключений — наследников IllegalFormatException (он наследует IllegalArgumentException).

String.format("%d", 3.5);         // IllegalFormatConversionException: d != java.lang.Double
String.format("%s и %s", "A");    // MissingFormatArgumentException: Format specifier '%s'
String.format("%-05d", 7);        // IllegalFormatFlagsException: Flags = '-0'
String.format("%i", 7);           // UnknownFormatConversionException: Conversion = 'i'
String.format("Скидка 50%");      // UnknownFormatConversionException, нужно 50%%
String.format("%s", "a", "b");    // исключения нет: "a", лишний аргумент игнорируется
String.format("%d", (Integer) null); // исключения нет: "null"

Флаги - или 0 без ширины дают MissingFormatWidthException. В Java нет %i и %u из C — целые выводятся только через %d.

5. 

Чем DecimalFormat и MessageFormat отличаются от String.format() и какие у MessageFormat есть подводные камни?

Оба класса из пакета java.text. DecimalFormat задаёт формат числа шаблоном вида #,##0.00, а MessageFormat подставляет аргументы по номерам в фигурных скобках {0}, {1} вместо спецификаторов % — так устроены файлы локализации интерфейса.

DecimalFormat df = new DecimalFormat("#,##0.00",
        DecimalFormatSymbols.getInstance(Locale.US));
df.format(1234567.891); // 1,234,567.89

MessageFormat.format("Файл {0} содержит {1} строк", "data.txt", 1500);
// Файл data.txt содержит 1,500 строк
MessageFormat.format("Файл {0} содержит {1,number,#} строк", "data.txt", 1500);
// Файл data.txt содержит 1500 строк

Подводные камни MessageFormat: числа в {1} форматируются по локали с разделителями групп (1500 → 1,500; вывод показан для Locale.US), без разделителей — формат {1,number,#}. Одинарная кавычка в шаблоне служит экранирующим символом, поэтому апостроф нужно удваивать.

Страница 1 из 1