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 methodsprint,printlnandprintf. - Format string
- A template like
"%s scored %d%n"with blanks (specifiers) thatprintffills 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,0pad 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:
\non Linux and macOS,\r\non 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.
System.out.print("Loading");
System.out.print("...");
System.out.println(" done"); // Loading... done
System.out.println(); // blank line02Your 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.
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.
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).
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
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%-10sto%-18s, fix the dash line and total widths to match, and run again. - 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
Trigger a format error
In the specifier tour, change
%don theintline to%fand 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.1415932 places 3.140 places 3science 1.234568e+04int Exception in thread "main" java.util.IllegalFormatConversionException: f != java.lang.Integer
Code & diagrams
Expected output
Hello, world
42
3.0
A
66
Java
Asha scored 92 pointsThe decimal point is the one used by your machine's locale; on an English-language setup it is a dot.
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 worldText columns use `-` to left-align; number columns right-align so the decimal points line up. `10 + 1 + 5 + 1 + 9 = 26` characters per row.
Expected output
Item Qty Price
Pen 3 10.50
Notebook 12 45.00
Bag 1 899.99
--------------------------
Total 1471.49Expected 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,89System.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.
public class Main {
public static void main(String[] args) {
double price = 49.5;
System.out.printf("Price: %d%n", price);
}
}Break #2
Forget a value
Write two specifiers but pass one value.
public class Main {
public static void main(String[] args) {
String name = "Asha";
System.out.printf("%s scored %d%n", name);
}
}Break #3
A lone % at the end
Print a percentage with "Progress: %d%".
public class Main {
public static void main(String[] args) {
int done = 45;
System.out.printf("Progress: %d%", done);
}
}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.
- ▸
%.NfusesRoundingMode.HALF_UP, whilejava.text.DecimalFormatdefaults toHALF_EVEN(banker's rounding):String.format("%.0f", 2.5)gives3butnew DecimalFormat("0").format(2.5)gives2. Know which one your reports use. - ▸
Every
String.formatcall parses the format string and creates a newFormatterandStringBuilder, so it is much slower than+concatenation in hot loops. For millions of lines, build output in aStringBuilderor wrapSystem.outin aPrintWriterover aBufferedWriterand flush once; everySystem.outcall 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.encodingsystem property; characters outside it print as?. JEP 400 (Java 18) made UTF-8 the default for files, not for the console. - ▸
%scallsString.valueOf(arg), so it printsnullfor null and usestoString()otherwise; if the argument implementsjava.util.Formattable, itsformatTomethod is called instead, which is how a class can support width and precision flags itself.
Remember this
- 1
System.outis an object of typePrintStreamconnected to the program's standard output (usually the terminal).print(x)writesxas text and stays on the same line;println(x)writesxand then a line separator;println()with nothing writes just the line break. Both accept every primitive type,String,char[]and any object (they call itstoString()). - 2
printf(format, args...)takes a format string with format specifiers, each starting with%.%dtakes a whole number,%fa decimal,%sanything (as text),%cachar,%ba boolean,%xhexadecimal,%escientific notation,%na line break and%%a literal percent sign. The values after the format fill the specifiers left to right. - 3
Between
%and the letter you can add flags, width and precision:%8dpads to 8 characters on the left (right-aligned),%-8spads on the right (left-aligned),%08dpads with zeros,%+dalways shows the sign,%,dadds grouping separators, and%.2frounds to 2 decimal places.%10.3fmeans 'at least 10 wide, 3 decimals'. This is how you print tidy tables. - 4
printfdoes not add a line break. End the format with%n, which writes the platform's line separator (\r\non Windows,\nelsewhere), rather than\n. Getting a specifier wrong is a run-time error, not a compile error:%dwith adoublethrowsIllegalFormatConversionException, and too few values throwsMissingFormatArgumentException. - 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):%,.2fprints1,234,567.89in the US and1.234.567,89in Germany. Pass aLocaleas the first argument when the output must not depend on the machine. - 6
%.2frounds half up (0.125becomes0.13) but only changes how the number is shown, never the value stored in the variable. For money, keep exact values inlongcents orBigDecimal(Topic 1.4) and format only at the end. Phase 6.6 covers formatting in depth, includingDecimalFormat, and Topic 6.5 covers multi-line text blocks.
Explain it without notes
What is the difference between print, println and printf?
Explain each part of the specifier %-12.3f, and what it prints for Math.PI.
Why doesn't the compiler catch printf("%d", 2.5), and what happens at run time?
Why should machine-readable output pass an explicit Locale to String.format?
Practice
Print a multiplication table for 7 from 1 to 5, with the numbers right-aligned in width 3, like 7 x 1 = 7.
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.
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
- ↔
printfkeeps 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.printlnis 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,printlnandprintf, and thatprintfneeds%n.I can use
%d,%f,%s,%c,%b,%x,%eand%%correctly.I can control width, alignment, zero padding, sign and decimal places to print an aligned table.
I can build formatted text with
String.formatandformatted.I can explain the three common format exceptions and fix each.
I know when to pass an explicit
Locale.