Formatting with formatted() and String.format

Format numbers, padding and columns with format specifiers, and keep machine-readable output stable by passing a locale.

SyntaxString.format(Locale.ROOT, "%-8s %5d %9.2f", sku, qty, total) "Order %s for %s".formatted(id, name)

String.format(pattern, args...) and the instance method pattern.formatted(args...), added in Java 15, both fill a template with values. They produce the same output; formatted reads better after a text block. System.out.printf uses the same rules and prints directly.

Specifiers you will use

SpecifierMeaningExample result
%sAny value via toString, null as nullAda
%dInteger types42
%.2fFloating point or BigDecimal, 2 decimals, half-up rounding19.99
%,dGrouping separators1,234,567
%8s, %-8sPad to width 8, right or left aligned     Ada
%06dZero-pad to width 6000042
%xHexadecimalff
%%, %nA literal %, the platform line separator

Locale changes the output

Both methods use the JVM's default locale unless told otherwise. On a server with a German default, String.format("%.2f", 1.5) gives 1,50, which breaks a CSV file, a JSON number or a value another service parses. For anything a program will read, call String.format(Locale.ROOT, ...). formatted has no locale parameter, so keep it for text people read. Even %d is not safe in machine output: a locale with its own digits, such as th-TH-u-nu-thai, prints 42 as Thai numerals.

Mistakes that only fail at run time

The compiler does not check patterns against arguments. %d with a double throws IllegalFormatConversionException, and too few arguments throws MissingFormatArgumentException. Too many arguments are silently ignored. Cover formatting code with a test.

%n is \r\n on Windows. In HTTP bodies, JSON and files with a defined format, write \n explicitly.

Money and performance

Format money from a BigDecimal, not a double; %.2f accepts both, but only the first holds the exact amount. Formatting parses the pattern on every call, so it is slower than concatenation. That does not matter for a response, but in a hot loop or a log statement that is usually disabled, avoid building the String at all; loggers take their own placeholders for that reason.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Render a plain-text order summary with aligned SKU, quantity and total columns for a confirmation email.
  • Write amounts to a CSV export with String.format(Locale.ROOT, "%.2f", total) so every server produces the same file.
  • Generate zero-padded, human-readable reference numbers such as INV-000042 for invoices.

More examples

Truncating, grouping and signs

A precision on %s cuts the text to that many characters, so %-10.10s gives a column that is exactly ten wide whatever the input. With Locale.ROOT the grouping separator is always a comma, whichever locale the server runs with.

Example · java
void main() {
    IO.println(String.format(Locale.ROOT, "[%.3s]", "Alexander"));
    IO.println(String.format(Locale.ROOT, "[%-10.10s]", "A very long product name"));
    IO.println(String.format(Locale.ROOT, "%,d", 1234567));
    IO.println(String.format(Locale.ROOT, "%+d / %+d", 5, -5));
}
// Output:
// [Ale]
// [A very lon]
// 1,234,567
// +5 / -5

Padding, zero-fill, hex and literal percent

Zero-padding gives fixed-width reference numbers, and %08x is a common way to show a hash or checksum. A single % must be written as %%, or the formatter treats it as the start of a specifier.

Example · java
void main() {
    IO.println("ORD-%06d".formatted(42));
    IO.println("%x / %X / %08x".formatted(255, 255, 48879));
    IO.println("[%5s][%-5s]".formatted("ab", "ab"));
    IO.println("%d%% done".formatted(75));
    IO.println("%s and %s".formatted(null, true));
}
// Output:
// ORD-000042
// ff / FF / 0000beef
// [   ab][ab   ]
// 75% done
// null and true

Pattern mistakes fail at run time

The first two mistakes compile and only throw when the line runs, perhaps in a rarely used error path. The third is silent: the extra argument is ignored, so a missing placeholder can go unnoticed.

Example · java
void main() {
    try {
        IO.println(String.format("%d items", 2.5));
    } catch (IllegalFormatConversionException e) {
        IO.println("Bad conversion: " + e.getMessage());
    }
    try {
        IO.println(String.format("%s owes %s", "Ada"));
    } catch (MissingFormatArgumentException e) {
        IO.println("Missing: " + e.getMessage());
    }
    IO.println(String.format("%s", "Ada", "extra"));
}
// Output:
// Bad conversion: d != java.lang.Double
// Missing: Format specifier '%s'
// Ada

Discussion

  • Be the first to comment on this lesson.