Java 25 output formatting
Java's printf formatting syntax is still useful in Java 25, but small console programs no longer need to rely on System.out.printf() for formatted output.
Java 25 includes the java.lang.IO class for simple console input and output. Combine IO.println() with String.formatted() and you get a modern alternative that keeps the familiar Formatter syntax:
void main() {
var name = "Cameron";
var site = "TheServerSide";
IO.println(
"I like the articles %s writes on %S."
.formatted(name, site)
);
}The output is:
I like the articles Cameron writes on THESERVERSIDE.This article uses Java 25 compact source files and IO.println() for most examples. The underlying format specifiers are the same ones used by printf.
printf vs. String.formatted in Java 25
The traditional approach is still valid:
System.out.printf(
"Hello %s, you have %,d messages.%n",
"Cameron",
12345
);A modern Java 25 alternative is:
void main() {
var message = "Hello %s, you have %,d messages."
.formatted("Cameron", 12345);
IO.println(message);
}String.formatted() creates the formatted String, while IO.println() handles console output. This cleanly separates formatting from I/O.
Java format specifier syntax
The general Formatter syntax is:
%[argument_index$][flags][width][.precision]conversionThe conversion character determines how the corresponding argument is formatted.
| Specifier | Purpose |
|---|---|
%s | String representation |
%S | Uppercase String representation |
%d | Decimal integer |
%o | Octal integer |
%x | Hexadecimal integer |
%f | Decimal floating-point value |
%e | Scientific notation |
%c | Character |
%b | Boolean representation |
%% | Literal percent sign |
%n | Platform line separator |
Format Strings
Use %s for normal String output and %S for uppercase output:
void main() {
var language = "Java";
var version = "twenty five";
IO.println(
"%s %S".formatted(language, version)
);
}String width and alignment
Specify a minimum field width with a number. Strings are right-aligned by default:
void main() {
IO.println("|%15s|".formatted("Java"));
IO.println("|%-15s|".formatted("Java"));
}The - flag left-aligns the value.
String precision
For Strings, precision limits the maximum number of characters written:
void main() {
IO.println("%.5s".formatted("JavaScript"));
}Format integers
Use %d with integral values such as byte, short, int and long.
void main() {
int above = -98765;
long below = 54321L;
IO.println(
"%,d :: %+,d".formatted(above, below)
);
}With a U.S. formatting locale:
-98,765 :: +54,321The original version of this example claimed that %,d :: %d would produce zero padding and a plus sign. It does not. Those behaviors require explicit flags.
Integer flags
| Flag | Meaning | Example |
|---|---|---|
- | Left-align | %-10d |
+ | Always show sign | %+d |
0 | Zero-pad field | %010d |
, | Locale-sensitive grouping | %,d |
( | Negative values in parentheses | %(d |
space | Leading space for positive values | % d |
Decimal, octal and hexadecimal
void main() {
int value = 123456789;
IO.println("Decimal: %,d".formatted(value));
IO.println("Octal: %o".formatted(value));
IO.println("Hex: %x".formatted(value));
}
Java Formatter patterns provide width, alignment, grouping and padding options for integer values.
Format double and float values
Use %f for floating-point values. Precision controls the number of digits after the decimal point.
void main() {
double top = 1234.12345;
float bottom = 1234.12345f;
IO.println(
"%+,.3f :: %,.5f"
.formatted(top, bottom)
);
}Typical U.S.-locale output is:
+1,234.123 :: 1,234.12341The float output illustrates floating-point precision. A Java float cannot exactly represent all of the decimal digits in 1234.12345f.
The first pattern, %+,.3f, means:
+always displays the sign;,requests locale-sensitive grouping;.3displays three digits after the decimal point; andfselects decimal floating-point formatting.
Scientific notation
Use %e instead of %f for scientific notation:
void main() {
double top = 1234.12345;
float bottom = 1234.12345f;
IO.println(
"%+.3e :: %.5e"
.formatted(top, bottom)
);
}Do not combine the grouping flag , with scientific notation. Formatter does not support that combination and throws a FormatFlagsConversionMismatchException.
Width, precision and Formatter flags can produce aligned floating-point output.
Format char and boolean values
Use %c for characters and %b for Boolean-style output. Their uppercase forms, %C and %B, uppercase the formatted result.
void main() {
boolean flag = false;
char lower = 'a';
char unicode = '\u0077';
IO.println(
"%B :: %c :: %C"
.formatted(flag, lower, unicode)
);
}The original code assigned a floating-point literal to a char, which does not compile. The corrected example uses actual character values.
Modern date and time formatting
Formatter supports legacy %t date/time conversions, but modern Java applications should normally use the java.time API and DateTimeFormatter.
For example:
void main() {
var now = LocalDateTime.now();
var formatter = DateTimeFormatter.ofPattern(
"EEE MMM d, uuuu HH:mm:ss"
);
IO.println(now.format(formatter));
}A result might look like:
Fri Aug 14, 2026 14:30:45This is clearer and less error-prone than repeating a single date object several times in a Formatter call.
Time zones require a zone-aware type
A LocalDateTime deliberately contains no time-zone information. If you need a UTC offset or zone name, use a type such as ZonedDateTime:
void main() {
var now = ZonedDateTime.now();
var formatter = DateTimeFormatter.ofPattern(
"uuuu-MM-dd HH:mm:ss z XXX"
);
IO.println(now.format(formatter));
}This fixes another common mistake in older examples: attempting to format a timezone from LocalDateTime, which has no zone or offset.
Legacy %t date and time conversions
If you are maintaining existing Formatter code, common date/time conversions include:
| Specifier | Meaning |
|---|---|
%tH | Hour, 00 through 23 |
%tM | Minute |
%tS | Second |
%tA | Full weekday name |
%ta | Abbreviated weekday |
%tB | Full month name |
%tb | Abbreviated month |
%td | Day of month |
%tY | Four-digit year |
%ty | Two-digit year |
%tT | 24-hour time as HH:MM:SS |
%tz | Numeric UTC offset when the value contains zone information |
Format a console table with Java 25 IO
Width specifiers are especially useful for aligned console tables.
record Primitive(
String category,
String name,
int bits
) {}
void main() {
var types = List.of(
new Primitive("Floating", "double", 64),
new Primitive("Floating", "float", 32),
new Primitive("Integral", "long", 64),
new Primitive("Integral", "int", 32),
new Primitive("Integral", "char", 16),
new Primitive("Integral", "short", 16),
new Primitive("Integral", "byte", 8),
new Primitive("Boolean", "boolean", 1)
);
IO.println("--------------------------------");
IO.println(" Java's Primitive Types");
IO.println("--------------------------------");
IO.println(
"| %-10s | %-8s | %4s |"
.formatted("CATEGORY", "NAME", "BITS")
);
IO.println("--------------------------------");
for (var type : types) {
IO.println(
"| %-10s | %-8s | %04d |"
.formatted(
type.category(),
type.name(),
type.bits()
)
);
}
IO.println("--------------------------------");
}The output is:
--------------------------------
Java's Primitive Types
--------------------------------
| CATEGORY | NAME | BITS |
--------------------------------
| Floating | double | 0064 |
| Floating | float | 0032 |
| Integral | long | 0064 |
| Integral | int | 0032 |
| Integral | char | 0016 |
| Integral | short | 0016 |
| Integral | byte | 0008 |
| Boolean | boolean | 0001 |
--------------------------------
Formatter width, alignment and padding rules make fixed-width console tables straightforward.
Use Locale when formatting must be deterministic
Grouping separators, decimal separators and some uppercase conversions depend on the default formatting locale.
If a test or generated file requires predictable U.S. formatting, use String.format() with an explicit locale:
void main() {
var value = 1234567.89;
var output = String.format(
Locale.US,
"%,.2f",
value
);
IO.println(output);
}Java 25 formatting cheat sheet
| Goal | Pattern |
|---|---|
| String | %s |
| Uppercase String | %S |
| Decimal integer | %d |
| Grouped integer | %,d |
| Hexadecimal integer | %x |
| Octal integer | %o |
| Two decimal places | %.2f |
| Scientific notation | %e |
| Character | %c |
| Boolean | %b |
| Minimum width 15 | %15s |
| Left alignment | %-15s |
| Zero padding | %010d |
| Always show sign | %+d |
The modern Java 25 approach is straightforward: keep using Formatter-style patterns where they make output clearer, create the String with formatted() or String.format(), and send the finished value to IO.println().
System.out.printf() remains valid, but String.formatted() plus IO.println() gives small Java 25 programs cleaner syntax and separates formatting from console output.