1. Overview
The String.format() method formats and returns a given String according to prespecified rules.
In this guide, we’ll understand the syntax, details, basic and advanced usage, as well as some common exceptions around the String.format() method.
2. Method Syntax
The String.format() method is overloaded with two versions:
public static String format (String format, Object... args)
public static String format (Locale l, String format, Object... args)
To begin with, let’s list each argument along with its purpose:
- format (type String): specifies a format string
- args (type Object): specifies the arguments referenced by the format specifiers in the format string
- l (type Locale): indicates the locale for the formatting
The number of args should match the number of specifiers in the format string; extra arguments, if any, are ignored.
Further, when using String.format() in Java, the order of arguments provided to the method must correspond to the order of the format specifiers in the format string. Specifically, the first argument maps to the first % specifier, the second argument to the second % specifier, and so on.
However, the Java Formatter syntax, which String.format() utilizes, also supports positional arguments. This enables explicit referencing of arguments by their index, overriding the default sequential matching.
To use positional arguments, we include an argument_index followed by a $ sign within the format specifier, such as %1$s to refer to the first argument or %2$d to refer to the second argument. This enables reordering or reusing arguments within the format string without changing their order in the argument list.
To apply a locale other than the default locale, we can use the method version that specifies Locale as the first argument.
3. Format Specifiers
Format specifiers have their own syntax, which varies depending on the type of argument they apply to. The only requirement is that each format specifier include a conversion that is to be applied to the argument.
The set of valid conversions for a given argument depends on the argument’s data type:
- For general, character, and numeric types, the conversion is a character indicating the argument formatting.
- For dates and times, the conversion is a two-character sequence with the first character as t or T and the second character indicating the format to be used.
- For format specifiers that don’t correspond to arguments, the conversion is a character indicating content to be inserted in the output.
Let’s see some common specifiers:
- %b: boolean representation
- %c: single character representation
- %d: decimal integer representation
- %x: hexadecimal integer representation
- %f: floating-point numbers (e.g., for formatting doubles)
- %e: exponential notation
The behavior of a null argument depends on the conversion. For example, characters s and S evaluate to null if the argument arg is null.
4. Basic Usage
Let’s demonstrate string formatting with an example JUnit test method:
@Test
public void whenFormat_thenCorrect() {
String value = "Baeldung";
String formatted = String.format("Welcome to %s!", value);
assertEquals("Welcome to Baeldung!", formatted);
boolean boolValue = true;
String formattedBool = String.format("Boolean: %b", boolValue);
assertEquals("Boolean: true", formattedBool);
char charValue = 'A';
String formattedChar = String.format("Character: %c", charValue);
assertEquals("Character: A", formattedChar);
int intValue = 255;
String formattedInt = String.format("Decimal: %d", intValue);
assertEquals("Decimal: 255", formattedInt);
String formattedHex = String.format("Hex: %x", intValue);
assertEquals("Hex: ff", formattedHex);
double floatValue = 123.456789;
String formattedFloat = String.format("Float: %.2f", floatValue);
assertEquals("Float: 123.46", formattedFloat);
String formattedExponential = String.format("Exponential: %e", floatValue);
assertEquals("Exponential: 1.234568e+02", formattedExponential);
}
The String.format() method also enables the use of multiple format specifiers in one go, which produces a single string that contains the formatted values:
String multipleFormat = String.format(
"Boolean: %b, Character: %c, Decimal: %d, Hex: %x, Float: %.2f, Exponential: %e",
boolValue, charValue, intValue, intValue, floatValue, floatValue
);
assertEquals("Boolean: true, Character: A, Decimal: 255, Hex: ff, Float: 123.46, Exponential: 1.234568e+02",
multipleFormat);
This way, we can compose a single string with different insertions and replacements.
5. Error Handling and Exceptions
Several errors and exceptions are relatively common when dealing with String.format().
5.1. Incompatible Flags
The FormatFlagsConversionMismatchException occurs when incompatible flags are provided for a format specifier. For example, using both %+ and %s (string conversion) together is invalid:
@Test
public void whenIncompatibleFlags_thenFormatFlagsConversionMismatchExceptionThrown() {
assertThrows(FormatFlagsConversionMismatchException.class, () -> {
String formatted = String.format("%+s", "Baeldung");
});
}
The above code throws an exception.
5.2. Precision Data Compatibility
On the other hand, IllegalFormatPrecisionException occurs when we attempt to use precision with a format type that doesn’t support it, such as integers:
@Test
public void whenInvalidPrecisionForType_thenIllegalFormatPrecisionExceptionThrown() {
assertThrows(IllegalFormatPrecisionException.class, () -> {
String formatted = String.format("%.2d", 123);
});
}
Since we’re dealing with the d formatting, the period flag causes an error.
5.3. Argument Count not Equal to Format Specifier Count
The MissingFormatArgumentException exception is thrown when the number of format specifiers exceeds the number of arguments passed to String.format(). For example, the issue arises when we provide one format specifier but no corresponding argument:
@Test
public void whenMissingFormatArgument_thenMissingFormatArgumentExceptionThrown() {
assertThrows(MissingFormatArgumentException.class, () -> {
String formatted = String.format("Welcome to %s and %s!", "Baeldung");
});
}
While there are more complex scenarios, these are usually common errors when using the method.
6. Padding an Integer With Leading Zeros
One common use case for the String.format() method is zero padding.
6.1. Basic Example
Let’s demonstrate padding an integer with leading 0s.
To that end, we define a method that takes an integer and the width of the output, and returns the integer padded with leading 0s:
private String padIntegerWithZeros(int number, int width) {
return String.format("%0" + width + "d", number);
}
The format specifier comprises several characters:
- %: introduces a format specifier
- 0: the output padding is zeroes
- <width>: the minimum number of characters to be written to the output
- d: the conversion character for a decimal integer
This way, CLI output can have a format that’s easier for visual inspection.
6.2. Negative Numbers
Let’s use a JUnit test method to pad a positive and negative integer:
@Test
public void givenAnInteger_whenPaddingWithZeros_thanIntegerGetsPadded() {
assertEquals("00000001", padIntegerWithZeros(1, 8));
assertEquals("-0000001", padIntegerWithZeros(-1, 8));
}
Notably, for negative numbers, the sign is still present, and padding occurs after the sign.
6.3. Issues and Considerations
When using String.format() to pad an integer with leading zeros, we might encounter the IllegalFormatConversionException. This exception typically occurs when we try to use a format specifier with a data type that isn’t compatible with it, e.g., trying to use %s (for string) with an integer, or %d (for decimal integer) with a string.
When padding integers, we should use d for the integer and 0 for the padding flag. A common mistake is trying to treat the integer as a string and then trying to pad it, or trying to convert an integer to a string using the Integer.toString() method.
7. Locale-Based Formatting
Locales define different conventions for separating thousands and decimals:
@Test
public void whenNumberFormatWithLocales_thenCorrect() {
String frenchFormatted = String.format(Locale.FRANCE, "%,f", 1234567.89);
assertEquals("1 234 567,890000", frenchFormatted);
String germanFormatted = String.format(Locale.GERMANY, "%,.2f", 1234567.89);
assertEquals("1.234.567,89", germanFormatted);
}
Locales influence how dates are presented, with various orderings of day, month, and year:
@Test
public void whenDateFormatWithLocales_thenCorrect() {
LocalDate date = LocalDate.of(2023, 9, 30);
DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy", Locale.US);
String usFormatted = date.format(usFormatter);
assertEquals("09/30/2023", usFormatted);
DateTimeFormatter germanFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale.GERMANY);
String germanFormatted = date.format(germanFormatter);
assertEquals("30.09.2023", germanFormatted);
}
Local conventions also impact how currency symbols and values look like, with different spacing and punctuation:
@Test
public void whenCurrencyFormatWithLocales_thenCorrect() {
NumberFormat usCurrencyFormat = NumberFormat.getCurrencyInstance(Locale.US);
String usFormatted = usCurrencyFormat.format(1000);
assertEquals("$1,000.00", usFormatted);
NumberFormat frenchCurrencyFormat = NumberFormat.getCurrencyInstance(Locale.FRANCE);
String frenchFormatted = frenchCurrencyFormat.format(1000);
assertEquals("1 000,00 €", frenchFormatted);
}
This way, we can directly incorporate locale settings within the formatting.
8. Conclusion
In this article, we learned about the syntax and use of the static method String.format().
Full examples from this article are available in the Maven-based project over on GitHub.