String Format

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 99 of 133

String.format builds a string from a format string containing placeholders, called format specifiers, and a list of values to put into them. It controls exactly how each value appears: how many decimal places, how wide a column is, whether numbers have thousands separators, how dates are written. The same mini-language is used by System.out.printf, String.formatted, PrintWriter.printf, and java.util.Formatter.

Three Ways to Format

String name = "Ada";
double total = 1499.5;

String a = String.format("Hi %s, your total is %.2f", name, total);
String b = "Hi %s, your total is %.2f".formatted(name, total);   // Java 15+
System.out.printf("Hi %s, your total is %.2f%n", name, total);    // prints directly

All three produce Hi Ada, your total is 1499.50. formatted is an instance method on the format string itself; printf writes to the stream instead of returning a string.

Anatomy of a Format Specifier

%[argument_index$][flags][width][.precision]conversion
PartExampleMeaning
%Starts a specifier
argument_index$2$Use the 2nd argument instead of the next one
flags-, 0, +, ,, (Alignment, padding, sign, grouping
width10Minimum number of characters
.precision.2Decimal places for numbers, maximum length for strings
conversiond, f, s, ...The type of value and how to render it

Only % and the conversion are required. The order of the optional parts is fixed: writing %12,.2f instead of %,12.2f is an error.

Conversions

ConversionTypeExampleResult
%sAny value (toString)format("%s", List.of(1, 2))[1, 2]
%SAny value, uppercasedformat("%S", "hi")HI
%dIntegers (int, long, BigInteger, ...)format("%d", 42)42
%fFloating point, fixed notationformat("%.3f", Math.PI)3.142
%eScientific notationformat("%.3e", 12345.678)1.235e+04
%x / %XHexadecimalformat("%x", 255)ff
%oOctalformat("%o", 8)10
%cCharacterformat("%c", 'z')z
%bBoolean (false for null)format("%b", null)false
%nPlatform line separator\n or \r\n
%%A literal percent signformat("%d%%", 45)45%

%f without a precision shows six decimal places: format("%f", 2.5) gives 2.500000.

The argument type must suit the conversion. %d with a double throws IllegalFormatConversionException; too few arguments throws MissingFormatArgumentException. These are runtime errors, so test format strings, especially rarely used ones such as error messages.

Width and Alignment

Width sets the minimum number of characters. Values are right-aligned by default; the - flag left-aligns them:

String.format("[%5d]", 42);       // [   42]
String.format("[%-5d]", 42);      // [42   ]
String.format("[%8s]", "ab");     // [      ab]
String.format("[%-8s]", "ab");    // [ab      ]
String.format("[%10.3f]", Math.PI);   // [     3.142]

Longer values are never cut; they simply exceed the width. For strings, precision truncates: format("%.3s", "abcdef") gives abc.

Width and alignment are how console reports and text receipts line up their columns.

Flags

FlagEffectExampleResult
-Left-align within the width%-6d with 4242
0Pad numbers with leading zeros%05d with 4200042
+Always show the sign%+d with 42+42
,Locale-specific grouping separators%,d with 10000001,000,000
(Negative numbers in parentheses%(,.2f with -1234.5(1,234.50)
(space)A leading space for positive numbers% d with 42 42
#Alternate form (0x prefix for hex)%#x with 2550xff

Flags combine: %,12.2f is "group thousands, at least 12 wide, 2 decimals." %08.2f with 3.14159 gives 00003.14.

Argument Indexes

Arguments are normally consumed in order. An explicit index (n$) selects a specific argument, and < reuses the previous one:

String.format("%2$s %1$s %2$s", "a", "b");                    // "b a b"
String.format("%tY-%<tm-%<td", LocalDate.of(2026, 11, 12));    // "2026-11-12"

Locales

Decimal points, grouping separators, and month names depend on the locale. Without one, String.format uses the JVM's default locale, so the same code can produce different output on different machines:

String.format(Locale.US, "%,.2f", 1234567.891);        // 1,234,567.89
String.format(Locale.GERMANY, "%,.2f", 1234567.891);   // 1.234.567,89

Pass an explicit locale whenever the output is read by programs (CSV files, APIs, logs), so that a decimal comma never appears where a decimal point is expected. For currency amounts shown to users, NumberFormat.getCurrencyInstance(locale) adds the currency symbol and correct conventions; the Number Format lesson covers it.

Dates and Times

The %t conversions format date and time values, but for java.time types a DateTimeFormatter is usually clearer:

LocalDate d = LocalDate.of(2026, 11, 12);
String.format("%tB %<td, %<tY", d);                          // November 12, 2026
d.format(DateTimeFormatter.ofPattern("dd MMM yyyy"));        // 12 Nov 2026

Date formatting has its own lesson.

Format Versus Concatenation

String a = "Item: " + name + " | Qty: " + qty + " | Price: " + price;
String b = String.format("Item: %-12s | Qty: %3d | Price: %,10.2f", name, qty, price);

Formatting is preferable when output needs alignment, fixed decimals, padding, or grouping, and when the overall shape of a message should be easy to see. Concatenation is simpler for short messages with no formatting needs. Formatting is somewhat slower than concatenation because the format string is parsed each time, which only matters in very hot code paths.

Practical Example: A Formatted Payslip

import java.time.LocalDate;
import java.util.*;

public class Payslip {
    record Item(String label, double amount) { }

    public static void main(String[] args) {
        String name = "Meena Raghavan";
        LocalDate month = LocalDate.of(2026, 10, 1);
        List<Item> earnings = List.of(new Item("Basic", 62000), new Item("HRA", 24800), new Item("Special allowance", 13450.5));
        List<Item> deductions = List.of(new Item("Provident fund", 7440), new Item("Professional tax", 208.33), new Item("Income tax", 9875));

        String row = "%-22s %12s%n";
        String money = "%-22s %,12.2f%n";
        StringBuilder out = new StringBuilder();
        out.append(String.format("PAYSLIP %tB %<tY%n", month));
        out.append(String.format("Employee: %s (ID %06d)%n", name, 4217));
        out.append("-".repeat(35)).append('\n');
        out.append(String.format(row, "Earnings", "INR"));
        double gross = 0;
        for (Item i : earnings) { out.append(String.format(money, i.label(), i.amount())); gross += i.amount(); }
        out.append(String.format(row, "Deductions", ""));
        double ded = 0;
        for (Item i : deductions) { out.append(String.format(money, i.label(), -i.amount())); ded += i.amount(); }
        out.append("-".repeat(35)).append('\n');
        out.append(String.format(money, "Net pay", gross - ded));
        out.append(String.format("Deductions are %.1f%% of gross pay%n", ded / gross * 100));
        System.out.print(out);
    }
}

Output (with an English default locale):

PAYSLIP October 2026
Employee: Meena Raghavan (ID 004217)
-----------------------------------
Earnings                        INR
Basic                     62,000.00
HRA                       24,800.00
Special allowance         13,450.50
Deductions                         
Provident fund            -7,440.00
Professional tax            -208.33
Income tax                -9,875.00
-----------------------------------
Net pay                   82,727.17
Deductions are 17.5% of gross pay

Two reusable format strings, row and money, give every line the same layout: a 22-character left-aligned label followed by a 12-character right-aligned value. Because the decimal places and widths are fixed, the amounts line up in a column regardless of their size. Other specifiers used here:

  • %,12.2f adds grouping separators and two decimals.
  • %06d zero-pads the employee ID.
  • %tB %<tY prints the month name and year from a single LocalDate argument, using < to reuse it.
  • %% produces the literal percent sign.

On a machine with a different default locale, the separators and month name would change; passing a Locale as the first argument to String.format would fix them.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about String Format and get answers drawn from it.

Signed-in readers only.