Command Palette

Search for a command to run...

PHASE 1Beginner ~29 min· topic 12 of 14

Topic 1.12

Printing and Formatting Output

In one line

System.out.print writes text, println writes text and ends the line, and printf fills a template such as "%-10s %6.2f%n" with values, controlling width, alignment, decimal places and signs. String.format and formatted do the same but give you the String instead of printing it.

Think of it like this

A form with blanks. A school report card has printed boxes: NAME ____, MARKS ____. The office fills each box with a value, and the boxes keep every card neatly lined up. A format string is that form: %s and %d are the blanks, and the values you pass fill them in order.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Standard output
The main place a program writes its text. In a terminal it's the screen; it can also be redirected into a file.
PrintStream
The class of System.out. It turns values into text and writes them out, with methods print, println and printf.
Format string
A template like "%s scored %d%n" with blanks (specifiers) that printf fills with values.
Format specifier
A blank in a format string, starting with % and ending with a letter that says what kind of value goes there, such as %d.
Width
The minimum number of characters a filled blank takes up. Shorter values are padded with spaces.
Precision
For decimals, the number of digits after the point (%.2f). For text, the maximum number of characters shown.
Flag
An extra character after % that changes the look: - left-align, 0 pad with zeros, + show the sign, , group digits.
Locale
A language-and-country setting, such as US English or German, that decides details like the decimal point and digit grouping.
Line separator
The characters that end a line: \n on Linux and macOS, \r\n on Windows.

Step by step

01print, println and the empty println

print writes and leaves the cursor where it is, so the next output continues on the same line. println writes and then moves to a new line. println() with no argument just ends the current line, which is how you print a blank line.

Both are overloaded (Topic 3.4): there is a version for each primitive type, for char[], for String and for Object. That is why println('A') prints A but println('A' + 1) prints 66: the second argument is an int expression, so the int version runs.

Main.javawhole filejava
System.out.print("Loading");
System.out.print("...");
System.out.println(" done");   // Loading... done
System.out.println();          // blank line

02Your first printf

printf reads the format string from left to right. Ordinary characters are copied as they are. Each % specifier is replaced by the next value from the argument list, converted the way the letter says.

Under the hood the extra values are passed as an Object[] (varargs, Topic 3.5), so a primitive like 92 is boxed to an Integer first. That's also why the compiler can't check that %d gets a whole number: it only sees Objects.

Your first printfdiagram
Rendering diagram…

03Anatomy of a specifier

The full shape is %[argument_index$][flags][width][.precision]conversion. Only % and the conversion letter are required.

%-10s means: flag - (left-align), width 10, conversion s (string). %08.2f means: flag 0 (zero-pad), width 8, precision 2, conversion f, so -3.14159 prints as -0003.14. %2$s picks the second argument, which lets you reuse or reorder values.

Anatomy of a specifierdiagram
Rendering diagram…

04Lining up a table

Width is a minimum: values shorter than the width are padded, values longer than it are printed in full (and push the columns out of line). Pick widths a little larger than your longest value.

Convention: left-align text (%-10s) and right-align numbers (%5d, %9.2f) so that digits and decimal points line up under each other. Use the same widths in the header row as in the data rows.

05Rounding for display, not for storage

%.2f rounds the text it produces, using round-half-up: 2.5 with %.0f prints 3, 0.125 with %.2f prints 0.13. The double itself is unchanged, so later calculations still use the full value.

If you round twice (once per item, then the total), your printed total can differ from the sum of printed items by a cent. Keep full precision while calculating and round once when you print.

06Format into a String: String.format and formatted

When you need the text rather than printing it (to store it, return it from a method, or join it into something bigger), use String.format. It takes exactly the same specifiers.

Java 15 added the instance method formatted, so "%d items".formatted(n) is the same as String.format("%d items", n) and reads naturally at the end of a template. It's especially handy with text blocks (Topic 6.5).

Main.javawhole filejava
String label = String.format("%-6s|%4d", "Pens", 12);   // "Pens  |  12"
String id = "INV-%04d".formatted(7);                       // "INV-0007" (Java 15+)

07Locale: same number, different text

Grouping separators and decimal points depend on the locale. With no locale argument, printf and String.format use the default locale of the machine running the program, which comes from the operating system's settings.

For output that other programs read (CSV files, logs, JSON), always pass a fixed locale such as Locale.ROOT or Locale.US; otherwise a server in Germany writes 3,14 where a parser expects 3.14. For people, the user's locale is often exactly right.

08Standard error and printing arrays

System.err is a second PrintStream, for error messages. It usually appears on the same screen, but it is a separate stream: redirecting output to a file (java Main > out.txt) captures System.out only, and the two streams can appear interleaved in a surprising order.

Printing an array with println(numbers) shows something like [I@1b6d3586: the type code and a hash, not the contents (the hash changes from run to run). Use Arrays.toString(numbers) (Topic 3.10). The one exception is char[], which println prints as text.

Try it yourself

  1. 1

    Widen the table

    In the bill example, add an item called "Mechanical pencil". Run it and watch the columns break: the name is longer than the width 10. Change every %-10s to %-18s, fix the dash line and total widths to match, and run again.

  2. 2

    Predict the padding

    Before running, write down exactly what each line prints, counting spaces: printf("[%6d]%n", 42), printf("[%-6d]%n", 42), printf("[%06d]%n", 42), printf("[%6.1f]%n", 3.14159).

    Answers: [ 42], [42 ], [000042], [ 3.1].

  3. 3

    Trigger a format error

    In the specifier tour, change %d on the int line to %f and run. The program compiles but stops at that line. Read the exception name: it tells you which conversion didn't match which type.

    terminal
    $ java Main
    ── expected output ──
    default 3.141593
    2 places 3.14
    0 places 3
    science 1.234568e+04
    int Exception in thread "main" java.util.IllegalFormatConversionException: f != java.lang.Integer

Code & diagrams

print, println and a first printf New tab
Sign in to run this example in your browser.

Expected output

Hello, world

42
3.0
A
66
Java
Asha scored 92 points
A tour of the specifiers New tab

The decimal point is the one used by your machine's locale; on an English-language setup it is a dot.

Sign in to run this example in your browser.

Expected output

default   3.141593
2 places  3.14
0 places  3
science   1.234568e+04
int       42
sign      +42
zeros     00042
hex       ff
char      Z
boolean   true
percent   75%
anything  7 2.5 null
cut text  abc
reorder   hello world
A neatly aligned bill New tab

Text columns use `-` to left-align; number columns right-align so the decimal points line up. `10 + 1 + 5 + 1 + 9 = 26` characters per row.

Sign in to run this example in your browser.

Expected output

Item         Qty     Price
Pen            3     10.50
Notebook      12     45.00
Bag            1    899.99
--------------------------
Total              1471.49
String.format, formatted and locales Java 15+ New tab
Sign in to run this example in your browser.

Expected output

Ravi is 12 years old
Order #00042
US:      1,428,627,663
Germany: 1.428.627.663
US:      1,234,567.89
Germany: 1.234.567,89
Standard error and arrays (fragment)java
System.err.println("Warning: file not found");   // goes to standard error, not standard output

int[] numbers = {3, 1, 2};
System.out.println(numbers);                    // [I@<hash>  - type and hash, changes every run
System.out.println(java.util.Arrays.toString(numbers));   // [3, 1, 2]

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use %d for a double

Print a double price with %d.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        double price = 49.5;
        System.out.printf("Price: %d%n", price);
    }
}
terminal
$ javac Main.java
java Main
── what you'll see ──
Price: Exception in thread "main" java.util.IllegalFormatConversionException: d != java.lang.Double
at java.base/java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4515)
at java.base/java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:3066)
at java.base/java.util.Formatter$FormatSpecifier.print(Formatter.java:3021)
at java.base/java.util.Formatter.format(Formatter.java:2791)
at java.base/java.io.PrintStream.implFormat(PrintStream.java:1367)
at java.base/java.io.PrintStream.format(PrintStream.java:1346)
at java.base/java.io.PrintStream.printf(PrintStream.java:1245)
at Main.main(Main.java:4)
Output from JDK 21. `Price: ` was already written before the bad specifier was reached. Line numbers inside the JDK differ between versions.

Break #2

Forget a value

Write two specifiers but pass one value.

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        String name = "Asha";
        System.out.printf("%s scored %d%n", name);
    }
}
terminal
$ javac Main.java
java Main
── what you'll see ──
Asha scored Exception in thread "main" java.util.MissingFormatArgumentException: Format specifier '%d'
at java.base/java.util.Formatter.format(Formatter.java:2790)
at java.base/java.io.PrintStream.implFormat(PrintStream.java:1367)
at java.base/java.io.PrintStream.format(PrintStream.java:1346)
at java.base/java.io.PrintStream.printf(PrintStream.java:1245)
at Main.main(Main.java:4)

Break #3

A lone % at the end

Print a percentage with "Progress: %d%".

Main.javawhole filejava
public class Main {
    public static void main(String[] args) {
        int done = 45;
        System.out.printf("Progress: %d%", done);
    }
}
terminal
$ javac Main.java
java Main
── what you'll see ──
Exception in thread "main" java.util.UnknownFormatConversionException: Conversion = '%'
at java.base/java.util.Formatter.parse(Formatter.java:2836)
at java.base/java.util.Formatter.format(Formatter.java:2774)
at java.base/java.io.PrintStream.implFormat(PrintStream.java:1367)
at java.base/java.io.PrintStream.format(PrintStream.java:1346)
at java.base/java.io.PrintStream.printf(PrintStream.java:1245)
at Main.main(Main.java:4)

Myth vs fact

Myth

printf adds a new line like println.

Fact

It writes only what the format says. End the format with %n.

Myth

%.2f rounds the variable to two decimals.

Fact

It rounds only the text it prints. The variable keeps its full value.

Myth

Format mistakes are compile errors.

Fact

javac doesn't check format strings. A wrong specifier compiles and then throws an IllegalFormatException subclass at run time (IDEs and linters can warn earlier).

Myth

%b prints whether a value is true.

Fact

For a boolean or Boolean it does. For any other non-null value it prints true (even for the String "false"), and for null it prints false.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    %.Nf uses RoundingMode.HALF_UP, while java.text.DecimalFormat defaults to HALF_EVEN (banker's rounding): String.format("%.0f", 2.5) gives 3 but new DecimalFormat("0").format(2.5) gives 2. Know which one your reports use.

  • ▸

    Every String.format call parses the format string and creates a new Formatter and StringBuilder, so it is much slower than + concatenation in hot loops. For millions of lines, build output in a StringBuilder or wrap System.out in a PrintWriter over a BufferedWriter and flush once; every System.out call takes a lock and writes through to the console.

  • ▸

    The characters you print are turned into bytes using the console's encoding, which on Windows is often a legacy code page rather than UTF-8. Recent JDKs expose it as the stdout.encoding system property; characters outside it print as ?. JEP 400 (Java 18) made UTF-8 the default for files, not for the console.

  • ▸

    %s calls String.valueOf(arg), so it prints null for null and uses toString() otherwise; if the argument implements java.util.Formattable, its formatTo method is called instead, which is how a class can support width and precision flags itself.

Remember this

  1. 1

    System.out is an object of type PrintStream connected to the program's standard output (usually the terminal). print(x) writes x as text and stays on the same line; println(x) writes x and then a line separator; println() with nothing writes just the line break. Both accept every primitive type, String, char[] and any object (they call its toString()).

  2. 2

    printf(format, args...) takes a format string with format specifiers, each starting with %. %d takes a whole number, %f a decimal, %s anything (as text), %c a char, %b a boolean, %x hexadecimal, %e scientific notation, %n a line break and %% a literal percent sign. The values after the format fill the specifiers left to right.

  3. 3

    Between % and the letter you can add flags, width and precision: %8d pads to 8 characters on the left (right-aligned), %-8s pads on the right (left-aligned), %08d pads with zeros, %+d always shows the sign, %,d adds grouping separators, and %.2f rounds to 2 decimal places. %10.3f means 'at least 10 wide, 3 decimals'. This is how you print tidy tables.

  4. 4

    printf does not add a line break. End the format with %n, which writes the platform's line separator (\r\n on Windows, \n elsewhere), rather than \n. Getting a specifier wrong is a run-time error, not a compile error: %d with a double throws IllegalFormatConversionException, and too few values throws MissingFormatArgumentException.

  5. 5

    String.format(format, args...) returns the formatted String instead of printing it; since Java 15 you can write the same thing as "Order #%05d".formatted(42). Numbers are formatted for a locale (a country and language setting): %,.2f prints 1,234,567.89 in the US and 1.234.567,89 in Germany. Pass a Locale as the first argument when the output must not depend on the machine.

  6. 6

    %.2f rounds half up (0.125 becomes 0.13) but only changes how the number is shown, never the value stored in the variable. For money, keep exact values in long cents or BigDecimal (Topic 1.4) and format only at the end. Phase 6.6 covers formatting in depth, including DecimalFormat, and Topic 6.5 covers multi-line text blocks.

Explain it without notes

01

What is the difference between print, println and printf?

02

Explain each part of the specifier %-12.3f, and what it prints for Math.PI.

03

Why doesn't the compiler catch printf("%d", 2.5), and what happens at run time?

04

Why should machine-readable output pass an explicit Locale to String.format?

Practice

01

Print a multiplication table for 7 from 1 to 5, with the numbers right-aligned in width 3, like 7 x 1 = 7.

02

Given double celsius = 36.6;, print the temperature in Fahrenheit (c * 9 / 5 + 32) with exactly one decimal place and a degree label, e.g. 36.6 C = 97.9 F.

03

Use String.format to build an invoice id INV-2026-00042 from the year 2026 and the number 42, store it in a variable, then print it and its length.

Trade-offs

  • ↔

    printf keeps layout and data separate and handles alignment and rounding, but its mistakes surface only at run time; + concatenation is checked by the compiler and faster, but turns messy for tables.

  • ↔

    Formatting with the default locale suits messages for the person at the keyboard; a fixed locale suits files, logs and APIs that other programs parse.

  • ↔

    System.out.println is fine for learning and small tools; services should log through a logging library, which adds timestamps, levels and output routing.

Done when you can

  • I know when to use print, println and printf, and that printf needs %n.

  • I can use %d, %f, %s, %c, %b, %x, %e and %% correctly.

  • I can control width, alignment, zero padding, sign and decimal places to print an aligned table.

  • I can build formatted text with String.format and formatted.

  • I can explain the three common format exceptions and fix each.

  • I know when to pass an explicit Locale.