Java String Formatting
Java String Formatting
Writing "Total: Rs." + amount + " for " + qty + " items on " + date works — but it becomes harder to read with every added field. String formatting gives you a template with labelled placeholders and fills them in cleanly. String.format("Total: Rs.%.2f for %d items on %s", amount, qty, date) reads like a sentence with blanks to fill, not a chain of plus signs and string fragments.
Java offers several ways to format strings: String.format() for creating formatted String objects, System.out.printf() for formatted console output, and Formatter for advanced formatting to any output target. All three use the same format specifier syntax — learn it once, use it everywhere.
Format Specifiers — The Building Blocks
A format specifier is a placeholder in a format string that gets replaced with a value. Every specifier starts with % and ends with a conversion character.
Anatomy of a format specifier: % [argument index$] [flags] [width] [.precision] conversion Examples: %s → String %d → Integer (decimal) %f → Floating-point %.2f → Floating-point, 2 decimal places %10s → String, right-aligned in 10 characters %-10s → String, left-aligned in 10 characters %05d → Integer, padded with zeros to width 5 %,d → Integer with thousand separators %n → Platform-specific newline %% → Literal % character Common conversion characters: s / S → String (S = uppercase) d → Decimal integer f → Floating-point e / E → Scientific notation b / B → Boolean c / C → Character x / X → Hexadecimal integer o → Octal integer n → Newline % → Literal percent sign
1 — String.format() — Creating Formatted Strings
String.format(template, args...) returns a formatted String without printing it. This is the most common formatting method in production code — used for building messages, log entries, API responses, report lines, and display values.
// File: StringFormatBasics.java
public class StringFormatBasics {
public static void main(String[] args) {
// String specifier — %s
String name = "Priya Sharma";
String role = "Senior Developer";
String result = String.format("Name: %s | Role: %s", name, role);
System.out.println(result);
// Integer specifier — %d
int orderId = 2024001;
int qty = 3;
System.out.println(String.format("Order #%d — Qty: %d", orderId, qty));
// Float specifier — %f and %.2f
double price = 1299.999;
System.out.println(String.format("Price: %f", price)); // 6 decimal places default
System.out.println(String.format("Price: %.2f", price)); // exactly 2 decimal places
System.out.println(String.format("Price: %.0f", price)); // rounded, no decimal
System.out.println();
// Boolean specifier — %b
boolean isActive = true;
System.out.println(String.format("Account active: %b", isActive));
// Character specifier — %c
char grade = 'A';
System.out.println(String.format("Grade: %c", grade));
// Hexadecimal — %x and %X
int colorCode = 16711680; // red in decimal
System.out.println(String.format("Color hex: #%X", colorCode)); // FF0000
System.out.println();
// Multiple values in one format string
String orderId2 = "ORD-2024-88291";
String customer = "Rohan Mehta";
double amount = 4999.50;
String status = "PLACED";
System.out.println(String.format(
"Order: %-20s | Customer: %-15s | Amount: Rs.%8.2f | Status: %s",
orderId2, customer, amount, status));
}
}Output:
Name: Priya Sharma | Role: Senior Developer
Order #2024001 — Qty: 3
Price: 1300.000000
Price: 1300.00
Price: 1300
Account active: true
Grade: A
Color hex: #FF0000
Order: ORD-2024-88291 | Customer: Rohan Mehta | Amount: Rs. 4999.50 | Status: PLACED
2 — Width and Alignment
Width controls the minimum number of characters a value occupies. Left-align with -, right-align (default), and pad with zeros using 0 flag.
// File: WidthAlignmentDemo.java
public class WidthAlignmentDemo {
public static void main(String[] args) {
// Right-aligned (default)
System.out.println(String.format("'%10s'", "Java")); // ' Java'
System.out.println(String.format("'%10d'", 42)); // ' 42'
System.out.println(String.format("'%10.2f'", 3.14)); // ' 3.14'
// Left-aligned — add - flag
System.out.println(String.format("'%-10s'", "Java")); // 'Java '
System.out.println(String.format("'%-10d'", 42)); // '42 '
System.out.println(String.format("'%-10.2f'", 3.14)); // '3.14 '
// Zero-padding — add 0 flag
System.out.println(String.format("'%010d'", 42)); // '0000000042'
System.out.println(String.format("'%010.2f'", 3.14)); // '0000003.14'
System.out.println();
// Building an aligned table
System.out.println(String.format("%-5s %-20s %10s %8s", "Sr.", "Product", "Price", "Stock"));
System.out.println("-".repeat(47));
Object[][] products = {
{1, "Wireless Headphones", "Rs.2,499", 150},
{2, "USB-C Hub", "Rs.899", 280},
{3, "Mechanical Keyboard", "Rs.3,999", 80},
{4, "Laptop Stand", "Rs.1,299", 200},
};
for (Object[] p : products) {
System.out.println(String.format("%-5d %-20s %10s %8d",
p[0], p[1], p[2], p[3]));
}
}
}Output:
' Java'
' 42'
' 3.14'
'Java '
'42 '
'3.14 '
'0000000042'
'0000003.14'
Sr. Product Price Stock
-----------------------------------------------
1 Wireless Headphones Rs.2,499 150
2 USB-C Hub Rs.899 280
3 Mechanical Keyboard Rs.3,999 80
4 Laptop Stand Rs.1,299 200
3 — Number Formatting
Numbers have special flags for thousand separators, sign display, and scientific notation.
// File: NumberFormattingDemo.java
import java.util.Locale;
public class NumberFormattingDemo {
public static void main(String[] args) {
double salary = 125000.75;
double bigNumber = 98765432.10;
int population = 1400000000;
double pi = Math.PI;
double tiny = 0.000001234;
// Thousand separators — , flag
System.out.println(String.format("Salary : Rs.%,.2f", salary));
System.out.println(String.format("Revenue : Rs.%,.0f", bigNumber));
System.out.println(String.format("Population : %,d", population));
System.out.println();
// Precision control
System.out.println(String.format("Pi (2dp) : %.2f", pi));
System.out.println(String.format("Pi (5dp) : %.5f", pi));
System.out.println(String.format("Pi (10dp) : %.10f", pi));
System.out.println();
// Scientific notation — %e
System.out.println(String.format("Tiny %%e : %e", tiny));
System.out.println(String.format("Tiny %%.2e : %.2e", tiny));
System.out.println(String.format("Big %%E : %E", bigNumber));
System.out.println();
// Sign display — + flag (always show sign)
System.out.println(String.format("Positive: %+.2f", 99.5));
System.out.println(String.format("Negative: %+.2f", -45.0));
System.out.println();
// Leading space for positive (aligns with negative)
System.out.println(String.format("Profit: % .2f", 12500.0));
System.out.println(String.format("Loss : % .2f", -3200.0));
System.out.println();
// Locale-specific formatting (comma vs dot as decimal separator)
System.out.println(String.format(Locale.US, "US format : %,.2f", salary));
System.out.println(String.format(Locale.GERMAN, "German format: %,.2f", salary));
System.out.println(String.format(Locale.FRENCH, "French format: %,.2f", salary));
}
}Output:
Salary : Rs.1,25,000.75
Revenue : Rs.9,87,65,432
Population : 1,400,000,000
Pi (2dp) : 3.14
Pi (5dp) : 3.14159
Pi (10dp) : 3.1415926536
Tiny %e : 1.234000e-06
Tiny %.2e : 1.23e-06
Big %E : 9.876543E+07
Positive: +99.50
Negative: -45.00
Profit: 12500.00
Loss : -3200.00
US format : 125,000.75
German format: 125.000,75
French format: 125 000,75
The , flag in Indian locale produces Indian numbering (lakhs and crores). For proper Indian currency formatting, use Locale with NumberFormat or control grouping manually.
4 — printf() — Formatted Console Output
System.out.printf() is identical to System.out.print(String.format(...)) — it formats and prints directly without creating an intermediate String variable.
// File: PrintfDemo.java
public class PrintfDemo {
public static void main(String[] args) {
// printf — formats and prints directly
System.out.printf("Name: %s%n", "Sneha Rao"); // %n = newline
System.out.printf("Score: %d / %d%n", 87, 100);
System.out.printf("Average: %.2f%n", 87.0 / 100.0);
System.out.println();
// Build a receipt using printf
System.out.printf("%-30s%n", "RECEIPT — ZOMATO ORDER");
System.out.printf("%-30s%n", "-".repeat(30));
System.out.printf("%-22s %7s%n", "Item", "Price");
System.out.printf("%-30s%n", "-".repeat(30));
String[][] items = {
{"Chicken Biryani", "Rs.349"},
{"Garlic Naan x2", "Rs.120"},
{"Raita", "Rs.49"},
{"Mango Lassi", "Rs.89"},
};
for (String[] item : items) {
System.out.printf("%-22s %7s%n", item[0], item[1]);
}
System.out.printf("%-30s%n", "-".repeat(30));
System.out.printf("%-22s %7s%n", "TOTAL", "Rs.607");
System.out.printf("%-22s %7s%n", "Delivery Charge", "Rs.30");
System.out.printf("%-30s%n", "-".repeat(30));
System.out.printf("%-22s %7s%n", "GRAND TOTAL", "Rs.637");
}
}Output:
Name: Sneha Rao
Score: 87 / 100
Average: 0.87
RECEIPT — ZOMATO ORDER
------------------------------
Item Price
------------------------------
Chicken Biryani Rs.349
Garlic Naan x2 Rs.120
Raita Rs.49
Mango Lassi Rs.89
------------------------------
TOTAL Rs.607
Delivery Charge Rs.30
------------------------------
GRAND TOTAL Rs.637
5 — Argument Index — Reusing Arguments
The argument index %n$ lets you reference the same argument multiple times without repeating it in the argument list.
// File: ArgumentIndexDemo.java
public class ArgumentIndexDemo {
public static void main(String[] args) {
// Without argument index — normal left-to-right
System.out.println(String.format("%s bought %d items for Rs.%,.2f",
"Priya", 3, 1299.0));
// With argument index — reference arguments by position (1-based)
System.out.println(String.format(
"Order by %1$s. %1$s's total: Rs.%2$.2f. %1$s will receive a receipt.",
"Priya", 1299.0));
// %1$ refers to arg 1 ("Priya") — used three times
// %2$ refers to arg 2 (1299.0)
System.out.println();
// Practical use — multilingual templates where order varies
String template = "Dear %1$s, your order %2$s worth Rs.%3$.2f is confirmed.";
System.out.println(String.format(template, "Rohan", "ORD-001", 2499.0));
System.out.println(String.format(template, "Sneha", "ORD-002", 899.0));
}
}Output:
Priya bought 3 items for Rs.1,299.00
Order by Priya. Priya's total: Rs.1299.00. Priya will receive a receipt.
Dear Rohan, your order ORD-001 worth Rs.2499.00 is confirmed.
Dear Sneha, your order ORD-002 worth Rs.899.00 is confirmed.
6 — Formatted() — Java 15+
Java 15 introduced String.formatted() as an instance method — an alternative to String.format() that reads more naturally when the template is a variable.
// File: FormattedMethodDemo.java
public class FormattedMethodDemo {
public static void main(String[] args) {
// Traditional String.format()
String msg1 = String.format("Hello, %s! Your score is %d.", "Karan", 92);
System.out.println(msg1);
// Java 15+ String.formatted() — same result, different syntax
String msg2 = "Hello, %s! Your score is %d.".formatted("Karan", 92);
System.out.println(msg2);
System.out.println();
// Particularly clean with text blocks (Java 15+)
String template = """
Dear %s,
Your order %s has been placed successfully.
Total amount: Rs.%.2f
Expected delivery: %s
""";
String email = template.formatted(
"Ananya Iyer",
"ORD-2024-99812",
3499.50,
"Jan 18, 2024"
);
System.out.println(email);
}
}Output:
Hello, Karan! Your score is 92.
Hello, Karan! Your score is 92.
Dear Ananya Iyer,
Your order ORD-2024-99812 has been placed successfully.
Total amount: Rs.3499.50
Expected delivery: Jan 18, 2024
Format Specifiers — Complete Quick Reference Table
| Specifier | Type | Example | Output |
|---|---|---|---|
%s | String | "Hello %s".formatted("World") | Hello World |
%S | String uppercase | "%S".formatted("java") | JAVA |
%d | Decimal int | "%d".formatted(42) | 42 |
%05d | Zero-padded int | "%05d".formatted(42) | 00042 |
%,d | Int with separator | "%,d".formatted(1000000) | 1,000,000 |
%f | Float (6dp) | "%f".formatted(3.14) | 3.140000 |
%.2f | Float (2dp) | "%.2f".formatted(3.14159) | 3.14 |
%,.2f | Float + separator | "%,.2f".formatted(99999.9) | 99,999.90 |
%e | Scientific | "%e".formatted(12345.6) | 1.234560e+04 |
%10s | Right-aligned | "%10s".formatted("hi") | __________hi (8 spaces) |
%-10s | Left-aligned | "%-10s".formatted("hi") | hi__________ (8 spaces) |
%b | Boolean | "%b".formatted(true) | true |
%c | Character | "%c".formatted('A') | A |
%x | Hex lowercase | "%x".formatted(255) | ff |
%X | Hex uppercase | "%X".formatted(255) | FF |
%n | Newline | "Line1%nLine2" | Two lines |
%% | Literal % | "100%%" | 100% |
%+.2f | Always show sign | "%+.2f".formatted(5.0) | +5.00 |
Basic Example
public class Main {
public static void main(String[] args) {
String name = "Priya";
double price = 499.5;
// %s = string, %.2f = decimal with 2 places, %-10s = left-align width 10
String message = String.format("Name: %-10s Price: Rs.%.2f", name, price);
System.out.println(message);
// %10s right-aligns instead
System.out.printf("Name: %10s%n", name);
}
}Output
Name: Priya Price: Rs.499.50 Name: Priya
How it works:
- ›
%-10sreserves 10 characters fornameand left-aligns it, padding the remaining space with spaces after"Priya". - ›
%.2fformatspriceto exactly two decimal places, turning499.5into499.50. - ›
String.format()returns the finished text as aString, which is then printed. - ›
%10s(no-) reserves the same 10-character width but right-alignsname, padding with spaces before it — shown withprintf(), which formats and prints in one step.
Best Practices
Use String.format() when you need the formatted String for further use. Assign the result to a variable, pass it to a method, include it in a larger structure. printf() is fine for quick console output but it does not give you the String back.
Always use %n instead of \n in format strings. %n produces the platform-correct line separator — \r\n on Windows, \n on Unix/Linux/macOS. \n is always Unix-style. For portability, especially when generating text files or reports, %n is the safer choice.
Use %.2f consistently for currency values. A price shown as 1299.5 looks unfinished. %.2f always shows exactly two decimal places — 1299.50. For Indian currency with thousand separators, combine both: %,.2f gives 1,299.50.
Prefer String.formatted() over String.format() for template strings in Java 15+. "Dear %s, your order is ready.".formatted(name) reads more naturally than String.format("Dear %s, your order is ready.", name) — the template and its formatter call are together.
Common Mistakes
Mistake 1 — Argument Count Mismatch
// Too few arguments — throws MissingFormatArgumentException
String s = String.format("Name: %s | Age: %d | City: %s", "Priya", 24);
// Three specifiers but only two arguments — runtime exception
// Too many arguments — extra arguments silently ignored
String t = String.format("Name: %s", "Priya", "extra", 42);
// Only "Priya" is used — no error, but confusingMistake 2 — Wrong Type for Specifier
// %d expects an integer — double throws exception
String s = String.format("Price: %d", 1299.50); // IllegalFormatConversionException
// Use %f or %.2f for doubles
String s = String.format("Price: Rs.%.2f", 1299.50); // correctMistake 3 — Using \n Instead of %n for Cross-Platform Output
// Hard-coded Unix newline — wrong on Windows when writing to files
String report = String.format("Line1\nLine2\nLine3");
// Correct — platform-appropriate newline
String report = String.format("Line1%nLine2%nLine3");Mistake 4 — Forgetting %% for a Literal Percent Sign
// Single % followed by nothing valid — throws exception
String s = String.format("Discount: 20%"); // MissingFormatArgumentException
// Correct — use %% to produce a literal %
String s = String.format("Discount: 20%%"); // "Discount: 20%"Interview Questions
Q1. What is String.format() in Java and how does it work?
String.format(template, args...) takes a format string containing format specifiers — placeholders starting with % — and a list of arguments. It replaces each specifier with the corresponding argument, applying the width, precision, and formatting flags specified. The method returns a new formatted String without printing anything. Under the hood, it uses a Formatter object that writes to a StringBuilder. System.out.printf() does the same but writes directly to standard output rather than returning a String.
Q2. What is the difference between %s, %d, and %f in Java format strings?
%s formats any object as a String — it calls String.valueOf() on the argument, which calls toString() for objects. %d formats an integer type — int, long, byte, short — as a decimal number. Passing a double to %d throws IllegalFormatConversionException. %f formats a floating-point number — float or double — with 6 decimal places by default. Use %.2f to control the number of decimal places. These are the three most used specifiers in production Java code.
Q3. How do you format a number with thousand separators and two decimal places in Java?
Use the %, flag combined with .2f precision: String.format("%,.2f", 125000.75) produces 125,000.75. For Indian number formatting (lakhs and crores), use Locale with NumberFormat.getCurrencyInstance(new Locale("en", "IN")) or manually format with String.format which uses the JVM's default locale for the , flag. The default locale on Indian JVMs typically uses Indian grouping.
Q4. What is the difference between System.out.printf() and String.format()?
Both use the same format string syntax and produce the same formatted output. The difference is what they do with it: String.format() returns the formatted text as a String object that you can assign, pass, or store. System.out.printf() writes the formatted text directly to standard output and returns the PrintStream — you cannot capture the formatted String from it. Use String.format() when you need the String for further processing. Use printf() for quick console output.
Q5. How do you left-align and right-align strings with String.format()?
Width alone — %10s — right-aligns the value in a 10-character field. The - flag — %-10s — left-aligns in the same field. For numbers, %10d right-aligns a decimal integer. %010d right-aligns with zero padding. The width specifies the minimum field width — if the value is wider, it is not truncated. The value expands the field. This is how consistently aligned table columns are built: the same width and alignment specifier on every row produces perfectly columnar output.
Q6. What exception is thrown when a format specifier does not match its argument type?
java.util.IllegalFormatConversionException is thrown at runtime when the type of an argument does not match what the specifier expects — for example, passing a double to %d (which expects an integer) or passing a non-boolean to %b. java.util.MissingFormatArgumentException is thrown when there are fewer arguments than format specifiers. Both are runtime exceptions — they are not caught at compile time, so incorrect format strings only fail when executed.
FAQs
Can String.format() handle null arguments?
Yes. For %s, a null argument is formatted as the string "null" — no exception. For other specifiers like %d and %f, null throws NullPointerException — these specifiers require numeric types and cannot represent null. If a numeric field might be null, use %s with String.valueOf(value) or provide a default: value != null ? value : 0.
Is String.format() slow compared to concatenation?
For simple expressions outside loops, the performance difference is negligible. String.format() does parse the format string and uses a Formatter internally, which is slightly more work than a plain +. For tight loops that format thousands of strings, the overhead can add up. In those cases, pre-build the format pattern once and reuse it, or use StringBuilder with manual appends. For most production code — building one or two messages per request — String.format() is perfectly fast.
Can you use format specifiers in log4j or SLF4J log statements?
Logging frameworks like SLF4J use a different placeholder syntax — {} — not %s. logger.info("Processing order {}", orderId) is the SLF4J style. It is lazy — the message is only formatted if that log level is enabled, avoiding unnecessary String creation. String.format() always builds the String regardless of log level. Use {} placeholders in SLF4J/Logback/Log4j2 log statements. Use String.format() for building Strings that go elsewhere — response bodies, error messages, file content.
What does %n produce and why is it different from \n?
%n produces the platform-specific line separator: \r\n on Windows, \n on Linux and macOS. \n always produces a Unix-style newline regardless of platform. For console output, the difference rarely matters — terminals on all platforms handle both. For writing text files, using %n produces files that open correctly in native text editors on all platforms.
Can String.format() be used to build SQL queries?
Technically yes — it can substitute values into a SQL template. But it should not be used for this in production code. Building SQL with string formatting is the classic path to SQL injection vulnerabilities. Use PreparedStatement with ? placeholders for all database queries. The %s specifier in a SQL template does not escape quotes or sanitise input — a malicious value like '; DROP TABLE users; -- would execute. PreparedStatement handles escaping correctly.
Summary
String formatting in Java centres on String.format() and the format specifier syntax. The specifiers — %s, %d, %f, %b, %c, %x, along with width, precision, and flag modifiers — give you full control over how each value appears in the output. printf() uses the same syntax for direct console output. formatted() (Java 15+) attaches the call directly to the template string.
The format specifiers that appear in almost every real codebase: %s for strings, %.2f for currency with two decimal places, %,d or %,.2f for numbers with thousand separators, %-Ns for left-aligned columns in tables, and %n for portable newlines.
For interviews, be ready to explain the difference between String.format() and printf(), demonstrate how to align a table using width and flag specifiers, explain what exceptions are thrown for mismatched types, and write a format string for Indian currency display.
What to Read Next
Take a closer look at how the String class really works.
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
Computes the area of a circle from its radius using the formula π × r².
Computes the circumference of a circle from its radius using the formula 2 × π × r.