String ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-16

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

Строка String.format("%.2f", 0.5) на компьютере с русскими региональными настройками вернёт не "0.50", а "0,50" — и следующий за ней Double.parseDouble(...) упадёт с NumberFormatException: For input string: "0,50". Чтобы такие сюрпризы не случались, нужно понимать, как устроено форматирование строк в Java.

Форматирование строк в Java — это построение строки по шаблону, в котором спецификаторы формата (%d, %s, %.2f и другие) заменяются значениями аргументов. Форматированный вывод выполняют методы String.format(), String.formatted() (Java 15+), System.out.printf() и класс java.util.Formatter, который лежит в основе их всех. Форматировать можно числа, строки, символы, даты и время.

String.format(), printf() и formatted(): какой метод выбрать

Если результат нужен как строка — используйте String.format() или "шаблон".formatted(); если его нужно сразу напечатать — System.out.printf(); если строку нужно собрать по частям или записать в файл — класс Formatter. Все эти методы понимают один и тот же синтаксис шаблона.

Метод Что возвращает С какой версии Когда использовать
String.format(шаблон, args...) Новую строку Java 5 Нужно получить строку (аналог sprintf из C)
"шаблон".formatted(args...) Новую строку Java 15 То же, но короче; удобно с текстовыми блоками
System.out.printf(...), System.out.format(...) Сам PrintStream, печатает в консоль Java 5 Форматированный вывод в консоль
PrintWriter.printf(...) Сам PrintWriter, пишет в поток Java 5 Запись в файл или сетевой поток
new Formatter(...).format(...) Сам Formatter, накапливает результат Java 5 Сборка текста из нескольких вызовов, запись в Appendable или файл

System.out — это статическая переменная типа PrintStream, поэтому у неё есть методы printf() и format(). Это два имени одного и того же метода: printf() добавлен для привычности программистам на C. Внутри все перечисленные методы используют Formatter.

Здесь и далее вывод в комментариях приведён для локали Locale.US: в русской локали вместо десятичной точки будет запятая (подробнее — в разделе Locale).

public class PrintfExample {
    public static void main(String[] args) {
        System.out.printf("Строка с форматированием: %.2f%n", 1000.0 / 3.0);
        System.out.format("%s, в следующем году вам будет %d%n", "Джон", 23);

        String str = String.format("Цена: %.2f руб.", 99.5);
        System.out.println(str);

        String greeting = "Привет, %s! У вас %d новых сообщений.".formatted("Анна", 5);
        System.out.println(greeting);
    }
}
// Вывод:
// Строка с форматированием: 333.33
// Джон, в следующем году вам будет 23
// Цена: 99.50 руб.
// Привет, Анна! У вас 5 новых сообщений.

Метод formatted() особенно удобен с текстовыми блоками (Java 15+): шаблон пишется как есть, без конкатенации и \n.

String json = """
        {"name": "%s", "age": %d}
        """.formatted("Анна", 30);
System.out.print(json);
// Вывод: {"name": "Анна", "age": 30}

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

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

%[argument_index$][flags][width][.precision]conversion
Часть Что задаёт Пример
argument_index$ Номер аргумента (с 1), заканчивается знаком $ %2$s — второй аргумент
flags Выравнивание, знак, ведущие нули, разделители групп %-10s, %,d, %+d
width Минимальная ширина поля в символах %8d
.precision Знаков после точки для чисел; максимальная длина для строк %.2f, %.3s
conversion Тип преобразования (обязателен) d, s, f, x

Индекс аргумента позволяет использовать одно значение несколько раз или менять порядок аргументов. Запись %<s означает «тот же аргумент, что в предыдущем спецификаторе»:

public class ArgumentIndexExample {
    public static void main(String[] args) {
        System.out.printf("Hello %1$s!%n%1$s, how are you?%n"
                        + "Welcome to the site %2$s%n",
                "John", "www.site.com");
        System.out.printf("%d в шестнадцатеричной системе: %<x%n", 255);
    }
}
// Вывод:
// Hello John!
// John, how are you?
// Welcome to the site www.site.com
// 255 в шестнадцатеричной системе: ff

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

Символ преобразования определяет, как интерпретируется аргумент: %d — целое число, %f — дробное, %s — строка, %c — символ, %b — логическое значение. Тип аргумента должен подходить к спецификатору, иначе будет исключение IllegalFormatConversionException.

Спецификатор Что выводит Пример Результат
%d Десятичное целое (int, long, BigInteger…) format("%d", 42) 42
%s / %S Строковое представление (toString()) format("%S", "java") JAVA
%f Дробное число в десятичной записи, по умолчанию 6 знаков после точки format("%f", 3.14159) 3.141590
%e / %E Дробное число в экспоненциальной (научной) записи format("%e", 12345.678) 1.234568e+04
%g / %G Дробное число: научная запись, если после округления значение меньше 10-4 или не меньше 10точность, иначе десятичная. Точность = общее число значащих цифр (по умолчанию 6) format("%g", 12345.678)
format("%g", 0.00001234)
12345.7
1.23400e-05
%a / %A Дробное число в шестнадцатеричной записи format("%a", 1.0) 0x1.0p0
%x / %X Целое в шестнадцатеричной системе format("%X", 255) FF
%o Целое в восьмеричной системе format("%o", 8) 10
%c / %C Символ (char или код символа int) format("%c", 65) A
%b / %B Логическое значение: для null — false, для Boolean — его значение, для любого другого объекта — true format("%b", null) false
%h / %H Хеш-код аргумента в шестнадцатеричном виде format("%h", "hi") d01
%t / %T Дата и время (с дополнительным суффиксом: %tY, %tF…) format("%tY", LocalDate.of(2026, 10, 16)) 2026
%n Перевод строки, принятый в операционной системе format("a%nb") a и b на разных строках
%% Сам знак % format("%d%%", 50) 50%

Заглавные варианты (%S, %X, %E, %G, %A, %B, %H, %C, %T) работают так же, как строчные, но переводят результат в верхний регистр: %x даёт ff, %X — FF; %b даёт true, %B — TRUE. Для %d, %f, %o заглавных вариантов нет.

Что выведет этот код?

System.out.printf("%b %b %b%n", "false", null, 0);

Ответ: true false true. Строка "false" — не Boolean, а обычный ненулевой объект, поэтому для %b она превращается в true. Число 0 тоже даёт true: в Java, в отличие от C, ноль не означает «ложь». И только null превращается в false.

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

Ширина (%10s) задаёт минимальное число символов в поле: короткое значение дополняется пробелами слева, а с флагом - — справа. Точность (%.2f) для дробных чисел задаёт число знаков после точки с округлением по правилу HALF_UP, а для строк (%.3s) — максимальную длину, лишнее отбрасывается.

public class WidthPrecisionExample {
    public static void main(String[] args) {
        double x = 1000.0 / 3.0;
        System.out.println("Без форматирования: " + x);
        System.out.printf("[%.2f]%n", x);
        System.out.printf("[%8.2f]%n", x);
        System.out.printf("[%-8.2f]%n", x);
        System.out.printf("[%10s][%-10s]%n", "Java", "Java");
        System.out.printf("[%.3s]%n", "Formatter");
        System.out.printf("[%3s]%n", "Formatter");
    }
}
// Вывод:
// Без форматирования: 333.3333333333333
// [333.33]
// [  333.33]
// [333.33  ]
// [      Java][Java      ]
// [For]
// [Formatter]

Последняя строка показывает неочевидный момент: ширина — это минимум, а не максимум. Если значение длиннее, оно выводится целиком, и колонки «разъезжаются». Чтобы обрезать строку, нужна точность: %-10.10s выведет ровно 10 символов.

Ширина и выравнивание пригодятся для вывода таблиц в консоль:

public class TableExample {
    public static void main(String[] args) {
        System.out.printf("%-10s%8s%n", "Товар", "Цена");
        System.out.printf("%-10s%8.2f%n", "Хлеб", 45.5);
        System.out.printf("%-10s%8.2f%n", "Молоко", 89.9);
    }
}
// Вывод:
// Товар         Цена
// Хлеб         45.50
// Молоко       89.90

Флаги формата

Флаги ставятся сразу после % (или после индекса аргумента) и управляют выравниванием, знаком числа, ведущими нулями и разделителями разрядов. Несколько флагов можно комбинировать: %,(.2f выведет отрицательное число в скобках и с разделителями групп.

Флаг Что делает Пример Результат
- Выравнивание по левому краю (требует ширину) format("[%-5d]", 42) [42 ]
0 Дополнение нулями вместо пробелов (только числа, требует ширину) format("%05d", 42) 00042
+ Всегда выводить знак числа format("%+d", 42) +42
пробел Перед положительным числом ставится пробел (для выравнивания с отрицательными) format("[% d]", 42) [ 42]
, Разделители групп разрядов по правилам локали format("%,d", 1234567) 1,234,567
( Отрицательные числа заключаются в скобки format("%(d", -42) (42)
# Альтернативная форма: префикс 0x для %x, 0 для %o format("%#x", 255) 0xff
public class FlagsExample {
    public static void main(String[] args) {
        System.out.printf("%,.2f%n", 10000.0 / 3.0);
        System.out.printf("%,(.2f%n", -10000.0 / 3.0);
        System.out.printf("%09.2f%n", 10000.0 / 3.0);
    }
}
// Вывод:
// 3,333.33
// (3,333.33)
// 003333.33

Несовместимые флаги вызывают исключение: + вместе с пробелом или - вместе с 0 дают IllegalFormatFlagsException, а - или 0 без ширины — MissingFormatWidthException.

Класс Formatter

Класс java.util.Formatter — базовый механизм форматированного вывода в Java: String.format() и printf() создают его внутри себя. Напрямую Formatter используют, когда текст нужно собрать за несколько вызовов format() или писать результат сразу в StringBuilder, файл или поток.

Метод format() класса Formatter преобразует аргументы по шаблону и добавляет результат к накопленному тексту; получить его можно через toString():

import java.util.Formatter;

public class FormatterExample {
    public static void main(String[] args) {
        Formatter formatter = new Formatter();
        formatter.format("This %s is about %n%S %c", "book", "java", '8');
        System.out.println(formatter);
    }
}
// Вывод:
// This book is about
// JAVA 8

Конструктор Formatter(Appendable, Locale) позволяет писать прямо в StringBuilder и сразу задать локаль. Formatter реализует Closeable, поэтому при записи в файл его закрывают в try-with-resources:

import java.util.Formatter;
import java.util.Locale;

public class FormatterBuilderExample {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        Formatter formatter = new Formatter(sb, Locale.US);
        formatter.format("%-8s%6.2f%n", "Чай", 3.5);
        formatter.format("%-8s%6.2f%n", "Кофе", 12.0);
        System.out.print(sb);
    }
}
// Вывод:
// Чай       3.50
// Кофе     12.00

Locale: точка или запятая

Форматирование чисел и дат зависит от локали: без явного Locale методы String.format() и printf() берут локаль системы (Locale.getDefault(Locale.Category.FORMAT)). В русской локали десятичный разделитель — запятая, а разделитель групп — неразрывный пробел, поэтому одна и та же программа выводит разные строки на разных компьютерах.

import java.util.Locale;

public class LocaleExample {
    public static void main(String[] args) {
        double price = 1234.567;
        Locale ru = Locale.forLanguageTag("ru-RU");

        System.out.println(String.format(Locale.US, "%,.2f", price));
        System.out.println(String.format(ru, "%,.2f", price));
        System.out.println(String.format(Locale.ROOT, "%.2f", price));
    }
}
// Вывод:
// 1,234.57
// 1 234,57
// 1234.57

Методы format() и printf() имеют перегрузку с первым параметром Locale. У formatted() такой перегрузки нет — он всегда использует локаль по умолчанию. Конструктор new Locale("ru", "RU") объявлен устаревшим в Java 19; вместо него используйте Locale.forLanguageTag("ru-RU") или Locale.of("ru", "RU") (Java 19+).

Совет

Для данных, которые будет читать программа (CSV, JSON, SQL, логи для парсинга), передавайте Locale.ROOT или Locale.US явно. Локаль по умолчанию оставляйте только для текста, который читает человек.

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

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

import java.time.LocalDate;

public class DateFormatExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.of(2026, 10, 16);
        System.out.println(String.format("%tF", date));
        System.out.println(String.format("%1$td.%1$tm.%1$tY", date));
    }
}
// Вывод:
// 2026-10-16
// 16.10.2026

Обратите внимание на %1$: без индекса каждый спецификатор %t ждёт свой аргумент, и шаблон "%td.%tm.%tY" с одной датой бросит MissingFormatArgumentException. Для сложных форматов дат в современном коде удобнее java.time.format.DateTimeFormatter.

На чём чаще всего ошибаются

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

Код Что произойдёт Как исправить
format("%d", 3.5) IllegalFormatConversionException: d != java.lang.Double %f для дробных или приведение (int)
format("%s и %s", "A") MissingFormatArgumentException: Format specifier '%s' Передать все аргументы или использовать %1$s
format("%-05d", 7) IllegalFormatFlagsException: Flags = '-0' Выбрать один флаг: %-5d или %05d
format("%i", 7) (привычка из C) UnknownFormatConversionException: Conversion = 'i' В Java целые выводятся только через %d
format("Скидка 50%") UnknownFormatConversionException Экранировать знак процента: 50%%
format("%s", "a", "b") Исключения нет, вывод a — лишний аргумент молча игнорируется Сверять число спецификаторов и аргументов (IDE подсвечивает)
format("%d", (Integer) null) Исключения нет, вывод null Проверять значение на null заранее

%n или \n?

В шаблонах используйте %n: он подставляет перевод строки текущей ОС (\r\n в Windows, \n в Linux и macOS). \n всегда даёт один символ LF — это правильно, только если формат файла или протокола требует именно его.

DecimalFormat и MessageFormat

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

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.MessageFormat;
import java.util.Locale;

public class TextFormatExample {
    public static void main(String[] args) {
        DecimalFormat df = new DecimalFormat("#,##0.00",
                DecimalFormatSymbols.getInstance(Locale.US));
        System.out.println(df.format(1234567.891));

        System.out.println(MessageFormat.format("Файл {0} содержит {1} строк", "data.txt", 1500));
        System.out.println(MessageFormat.format("Файл {0} содержит {1,number,#} строк", "data.txt", 1500));
    }
}
// Вывод:
// 1,234,567.89
// Файл data.txt содержит 1,500 строк
// Файл data.txt содержит 1500 строк

Нюанс MessageFormat: числа в {1} форматируются по локали с разделителями групп, поэтому 1500 превращается в 1,500. Формат {1,number,#} выводит число без разделителей. Ещё одна особенность: одинарная кавычка в шаблоне MessageFormat служит экранирующим символом, и чтобы вывести апостроф, её нужно удвоить ('').

Часто задаваемые вопросы

Есть ли в Java аналог функции sprintf из C?

Да, это String.format() (или "шаблон".formatted() в Java 15+): он возвращает отформатированную строку, а System.out.printf() соответствует printf из C. Синтаксис похож, но есть отличия: нет %i и %u, для перевода строки есть %n, а %b работает с любым объектом. Подробнее — в разделе На чём чаще всего ошибаются.

Чем printf() отличается от println()?

println() печатает одно значение и всегда добавляет перевод строки. printf() принимает шаблон и любое число аргументов, а перевод строки сам не добавляет — его нужно указать в шаблоне через %n.

Что быстрее: String.format() или конкатенация через +?

Конкатенация заметно быстрее: String.format() при каждом вызове разбирает шаблон и создаёт объект Formatter. В циклах и горячем коде собирайте строки через + или StringBuilder, а String.format() используйте там, где важны читаемость и выравнивание.

Как оставить два знака после запятой без округления?

%.2f всегда округляет по правилу HALF_UP: String.format("%.2f", 1.999) даст 2.00. Чтобы отбросить лишние знаки, используйте BigDecimal: new BigDecimal("1.999").setScale(2, RoundingMode.DOWN) вернёт 1.99.

Practice

Reinforce this lesson with hands-on tasks, each with a level and a time estimate.

All module tasks →

Video Explanation

Prefer video format? Watch this lesson with examples and explanations.

Comments

Please log in or register to have a possibility to add comment.