Comprehensive Guide to Java String Formatting - International Burch University
Peloton launches its first exercise game for connected bike owners
July 20, 2021
Getting Started With PrestoDB
July 20, 2021

Comprehensive Guide to Java String Formatting

Comprehensive Guide to Java String Formatting

Formatting Strings in Java can be a confusing and difficult task for beginners and experts alike. While the concept of String formatting is simple, there are nearly endless combinations that can be easy to forget and frustrating to look up. In this article, we will outline the basic techniques for formatting Strings in Java using the venerable C-style and walk through easy-to-digest tables for all of the conversions and format specifiers available to us (that can be used as reference tables when we inevitably forget which format specifiers to use).

While not the focus of this article, we will also touch on the MessageFormat class and how it can provide a simplified approach when the C-style approach is too cumbersome. Additionally, we will look at the various mechanisms we can use to format C-style conversions, including flags, widths, and precisions. Lastly, we will cover some important references that will help us to gain a deeper understanding of formatting Strings in Java.

Approaches to Formatting Strings in Java

Java was created at a time when C and C++ were the top-dogs in the programming arena, and, unsurprisingly, formatting Strings in Java resembles the style common in C. Since this is the oldest and most versatile approach provided by Java, we will focus on how we can use these formatting methods to create the Strings we desire. Nonetheless, a significant time has passed since the inception of Java and other approaches have been included in the language that trades the versatility of the C-style formatters for simplicity. One of the most common alternatives is the MessageFormat class. Although we will not focus on this class, we will touch on its basics and understand how it can be used as an alternative to the C-style approach for a wide variety of situations.

Methods and Classes

There are three common C-style methods used for formatting Java String objects:

  1. PrintStream.format: Formats a String object and prints it to a stream, such as Standard Output.
  2. String.format: Formats a String object and returns it as a new String object; this allows the results to be assigned to a variable.
  3. Fomatter.format: Formats a String object and writes it to the destination supplied in the constructor of the Formatter object.

When applicable, we can also use MessageFormat.format, which acts as a simplified version of the full-featured String formatting supported by the above format methods.


Each time we call System.out, we expect that we obtain a reference to standard output. If we look closely at this call, System.out simply provides a PrintStream object that maps to standard output. Therefore, we can write to standard output by calling the format method of the PrintStream class (i.e., System.out.format). We can also format Strings and write them to the other two standard PrintStreams — (standard input) and System.err (standard error) — in a similar manner. For example, we can write a formatted String to standard error using the System.err.format method. 

Note that the PrintStream class also provides a printf method that is identical to the format method, but keeps the printf naming familiar to C developers. Adding these options together, we get six different ways to write formatted Strings to the various standard PrintStreams:

  1. System.out.format
  3. System.err.format
  4. System.out.printf
  6. System.err.printf


Unlike the PrintStream variants, String.format does not write the formatted String directly to a stream (such as standard output). Instead, the String.format returns a new String that is the result of the formatting operation. For example:

String formattedString = String.format(…);

While the the PrintStream variants will be useful for beginners and those dealing directly with standard output, standard input, and standard error, it will be more common to format a String using the String.format method and capture the results in a new String for use at a later time.


The Formatter.format method is similar to the String.format method, but instead of returning the resulting String object, the Formatter.format method writes the resulting String object to the destination supplied in its constructor. The possible types of destinations include:

  • File
  • OutputStream
  • PrintStream
  • Appendable
  • A file corresponding to a String-based filename

For example, we can write a formatted String to a file foo.txt using the following:Java1

File file = new File("foo.txt");


Formatter formatter = new Formatter(file);


formatter.format("Hello, %s%n", "Justin");



If we look at the contents of the file foo.txt, we see that it contains our formatted String:Plain Text1

Hello, Justin

We will see shortly what %s and %n mean and how the format method works, but for now, we can see that we can write formatted Strings to a wide variety of different output destinations using the Formatter.format method. In many cases, the String.format method suffices by returning the formatted String — and we will focus primarily on this method throughout this article — but there are times when the flexibility provided by the Formatter.format method can be useful.


Unlike the previous three approaches, the MessageFormat.format method does not provide an open-ended mechanism that accepts any C-style formatters but instead provides a simplified formatting structure that allows us to specify which argument a specifier corresponds to and which pre-packaged format to use for each argument. For example, if we want to print the first argument — which is addressed using 0-indexing — as an integer and the second as a date, we can use the following:Java1

String formattedString = MessageFormat.format("Int: {0,number,integer}, date: {1,date}", 117, new Date());

This results in the following String being store to formattedString (the particular date will vary based on when the above snippet is run, but the formatting of the date will remain the same):Plain Text1

Int: 117, date: Jul 14, 2021

We can also store a MessageFormat instance for reuse using the following:Java1

MessageFormat format = new MessageFormat("Int: {0,number,integer}, date: {1,date}");


format.format(new Object[] { 117, new Date() });

This snippet produces the same result as our previous example. Note that the arguments must be supplied as an Object[] when using the instance format method. While the MessageFormat class can be useful for concatenating various arguments into a single String, it is limited in its ability to format different values. Unlike the previous three methods, the MessageFormat approach does not provide the fine-grained options to format a String precisely how we want. This results in an approach that is easier to use but also less robust.

In this article, we will focus on the C-style String formatting provided in Java. However, it is still important to understand how the MessageFormat class can be useful in a wide variety of situations and how it can be used to format Strings in a simplified manner. For more information, see the official MessageFormat documentation

Structure for C-Style Strings in Java

Regardless of which method we choose, formatting a String in Java using the C-style requires two parts:

  1. Format String: A String that specifies the format the resulting String should have. Format strings are a combination of fixed text and one or more format specifiers. Format specifiers act as placeholders and are replaced with the values provided in the argument list according to the formatting options we provide.
  2. Argument List: A list of values that will be used to compose the format string according to format specifiers provided.

For example:Java1

String formattedString = String.format("Hello, %s", "Justin");

When we execute the above snippet, the result Hello, Justin is stored in the formattedString variable. In this case, the String Hello, %s is our format string, while Justin is the only argument in the argument list. In particular, the format specifier %s acts as a placeholder for strings. Since we provided a %s format specifier in our format string, %s was replaced by our Justin String in the argument list, resulting in a formatted String of Hello, Justin.

Format Specifier in Action

In this case, there is a one-to-one correspondence between the format specifier and the arguments in the argument list (i.e., the first argument is in the argument list is used to replace the first format specifier). This is not always the case, though. The correspondence between the format specifiers we provide and their replacement with arguments from the argument list depends on the particular format specifiers we use. 

If a specifier requires an argument and one is not provided, a MissingFormatArgumentException is thrown. For example, the following throws an MissingFormatArgumentException:Java1


In general, there are three categories of format specifiers we can use:

  1. General, characters, and numeric types
  2. Dates and times
  3. Specifiers that do not require an argument

The Java data types that correspond to each of these categories are specified in the table below:

General, characters, numeric typesGeneralAny type
CharactercharCharacterbyteByteshortShort, and int and Integer when Character.isValidCodePoint(int) results in true
IntegerbyteByteshortShortintIntegerlongLong, and BigInteger
Floating PointfloatFloatdoubleDouble, and BigDecimal
Dates and timeslongLongCalendar, and Date.
Non-argument specifiers

Formatting General, Character and Numeric Strings

For general, character, and numeric types, format specifiers have the following format:Plain Text1



  • % denotes the start of a format specifier.
  • <index$> is an optional positive integer that corresponds to an argument in the argument list. This index starts at 1 (i.e., the first argument in the argument list is indexed as 1). Therefore, the first argument in the argument list can be addressed using 1$, the second argument can be addressed using 2$, and so on. For example, we can format a String — which has a conversion of s— that corresponds to the second argument in our argument list using the following format specifier:Java1%2$s
  • <flags> is an optional set of characters that modify the format, such as alignment and padding. The available flags depend on the data type of the supplied conversion. We will see a complete list of flags later.
  • <width> is an optional, non-negative integer that specifies the minimum width (in characters) of the result. If the result is less than the minimum width, padding (such as whitespace) will be provided. We will see more in-depth examples of width formatting later.
  • <precision> is an optional, non-negative integer that constrains the number of characters or significant digits. This behavior of the precision value varies based on the type of the supplied conversion. We will see more examples of specifying precision (where applicable) later.
  • <conversion> is a required character that specifies the formating for the supplied argument (i.e., a string, a hexadecimal value, a floating-point number, etc.). In our example above, the s in our %s format specifier denotes that the supplied argument is a string. Thus, the entire StringJustin, is used to replace the %s format specifier.

Using our s conversion, all of the following are examples of valid format specifiers:Plain Text1









A complete list of conversions is contained in the table below:

bBGeneralThe boolean result as a string (e.g., true or false) if the supplied argument is a Boolean. false if the supplied argument is null and true for all other arguments. If B is used, the resulting boolean value is capitalized (e.g., TRUE or FALSE).true
hHGeneralThe hexadecimal value of the supplied argument, arg, by applying the expression Integer.toHexString(arg.hashCode()). Note that the result does not include a 0x prefix. If H is used, the resulting alphabetic hexadecimal characters are capitalized.fff03
sSGeneralThe provided argument as a string (by invoking Object.toString). If the supplied argument is a Formattable object, the string is obtained by invoking Formattable.formatTo. If S is supplied, the supplied string is
cCCharacterThe Unicode character. If a String object is supplied, an IllegalFormatConversionException is thrown. If C is used, the supplied character is capitalized.c
dIntegerThe decimal integer.10
oIntegerThe octal integer.12
xXIntegerThe hexadecimal integer. Note that the result does not include a 0x prefix. This specifier should be preferred over h when supplying an integer. If X is used, the resulting alphabetic hexadecimal characters are capitalized.ff4
eEFloating PointThe value in scientific notation. If E is used, the exponent symbol (e) is capitalized (i.e., E).1.114313e+09
fFloating PointThe decimal number.4.562340
gGFloating PointThe value in scientific notation or decimal format, depending on the result after rounding. If G is used, the exponent symbol (e) is capitalized (i.e., E).128636
aAFloating PointThe value in scientific notation, where the significand is a hexadecimal floating point value, and the power follows a p delimiter. If A is used, the resulting alphabetic hexadecimal characters are capitalized and the p delimiter is capitalized (i.e., P).0x1.63c774a3d70a4p19


The following table contains examples of various conversions, flags, indexes, precisions, and widths that demonstrate how we can use format specifiers to obtain our desired String. The table is constructed with the left column representing the format string supplied to the String.format method, the center column representing the argument list supplied to String.format, and the right column representing the result (i.e., the formatted String) returned from the String.format method:Java1

String <result> = String.format(<format-string>, <arguments...>);

Note that the format strings and results in the table below are surrounded by quotes to represent the String returned from the String.format method and highlighted any formatting, such as trailing spaces, that are included in the String.

"Hello, my name is %s""Justin""Hello, my name is Justin"
"Name: %10s""Justin""Name:     Justin"
"Name: %-10s""Justin""Name: Justin    "
"%f times %f is %.2f"1.5, 6.784, 10.176"1.500000 times 6.784000 is 10.18"
"%d+%d=%d"2, 2, 4"2+2=4"
"The speed of light is %.3g m/s"299792458.0"The speed of light is 3.00e+08 m/s"
"The first letter of the alphabet is %c"'a'"The first letter of the alphabet is a"
"The hex value of %d is 0x%1$h"8756"The hex value of 8756 is 0x2234"

Formatting Date and Time Strings

Format specifiers for dates and times are similar to the generalized case above but are more constrained. Date and time format specifiers have the following format:Plain Text1


The %<index><flags>, and <width> components have the same meaning as they did previously, but the <conversion> is treated differently. To denote that the conversion should be a date or time, the character t or T is prepended to the conversion. This t or T prefix is followed by the format character for the date or time. Thus, the structure for a date or time format conversion is either:Plain Text1




For example, we can print a full month name using the B conversion (we will see a complete list of date and time conversions shortly):Plain Text1




When using a date or time conversion, the supplied argument must be a Java data type that encodes a date or time, such as a longLongCalendar, or Date. This argument is then formatted according to the supplied conversions. For example, we can use the date or time conversions, H an M, which represent the hour (in 24-hour format) and minutes, respectively — both with leading zeros if necessary — to format a supplied Date object:Java1

String.format("%1$tH %1$tM", new Date());

This snippet outputs the following (or similar, depending on the time it is executed):Plain Text1

08 50

By using the 1$ index, we instruct the format method to reuse our single argument (i.e., new Date()) when substituting both format specifiers. We can remove the indices by storing our argument in a variable and supplying it for each of the arguments (in our case, twice):Java1

Date d = new Date();


String.format("%tH %tM", d, d);

Both approaches can be clunky and cryptic. We can use a DateTimeFormatter (or similar class, such as SimpleDateFormat) to format dates and times in most cases. When possible, using a DateTimeFormatter (or similar class) is preferable, since the format can be stored and reused and the formatting is more direct, since we already know that we intend to format a single date (and do not have to include any t or T prefixes or indices). For example, we can format the above date using a SimpleDateFormat object without the need to index the argument list:Java1

SimpleDateFormat format = new SimpleDateFormat("HH mm");


String formattedString = format.format(new Date());

This snippet results in formattedString storing the same hours and minutes as our previous example (note that SimpleDateFormat has its own specifiers and the format specifiers HH and mm are different than the %H and %M in our previous example). However, we no longer need to use the format specifier prefix (%), indices, or repeated arguments in the argument list.


Nonetheless, dates and times can still be formatted through the C-style format methods, so we must know how to format them using conversions.


The complete list of date conversions is contained in the table below:

BFull-month name. The specific name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %TB results in a capitalized month name.January
hbAbbreviated name of the month. This specific name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifiers %Th and %Tb result in a capitalized, abbreviated month names.Jan
AFull day of the week. The specific day name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %TA results in a capitalized day name.Monday
aAbbreviated day of the week. The specific day name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %Ta results in a capitalized, abbreviated day name.Mon
CFour-digit year, divided by 100, padded with leading zeroes when necessary. This value ranges from 00 to 99.

For example, 19 for 1999 and 20 for 2000. This value can be thought of as the first two digits in the year, but note that all of the corresponding rules in Java for division apply here as the first two digits are obtained through division by 100.
YFour-digit year, formatted with leading zeroes when necessary. For example, the year 307 will result in 0307.2021
yTwo-digit year, ranging from 00 to 99. For example, 21 for 2021. If needed, the year will be padded with zeroes. For example, 05 for 2005.21
jDay of the year, formatted using three digits, including padding with zeroes when necessary. This value ranges from 001 (the first day of the year) to 366 for the Gregorian Calendar.005
mTwo-digit month, including leading zeroes when necessary. This value ranges from 01 (the first month) to 13.03
dTwo-digit day of the month, including leading zeroes when necessary. This value ranges from 01 (the first day of the month) to 31.04
eTwo-digit day of the month. This value ranges from 1 (the first day of the month) to 31.3


Similarly, a complete list of time conversions are specified in the table below:

HHour of the day in 24-hour format (i.e., military time) with leading zeroes when necessary. This value ranges from 00 to 23.03
IHour of the day in 12-hour format with leading zeroes as necessary. This value ranges from 01 to 12.03
kHour of the day in 24-hour format. This value ranges from 0 to 23.3
lHour of the day in 12-hour format. This value ranges from 0 to 12.3
MMinute of the hour with leading zeroes when necessary. This value ranges from 00 to 59.07
SSeconds of the minute with leading zeroes when necessary. This value ranges from 00 to 60, where 60 is a special value reserved for lead seconds.06
LThree-digit milliseconds of the second with leading zeroes when necessary. This value ranges from 000 to 999.040
NNine-digit nanoseconds of the millisecond with leading zeroes when necessary. This value ranges from 000000000 to 999999999.038518000
pMorning or afternoon marker (i.e., am or pm). This marker is locale-specific.

This marker can also be formatted to uppercase when used with the T prefix. For example, using the format specifier %tp produces either am or pm, while the format specifier %Tp produces either AM or PM.
zNumeric time zone offset from GMT. This offset follows the RFC 822 format is adjusted as necessary for Daylight Savings Time (DST). For non-time-zoned arguments, such as longLong, and Date, the default time zone for the current instance of the Java Virtual Machine (JVM) is used.

For more information on the default time zone, see TimeZone.getDefault().
ZAbbreviation for the time zone. This offset is adjusted as necessary for DST. For non-time-zoned arguments, such as longLong, and Date, the default time zone for the current instance of the JVM is used. Note that the supplied locale will supersede the locale of the corresponding argument being formatted in the argument list when a locale is provided.EDT
sSeconds since the beginning of the epoch starting January 1, 1970 at 00:00:00 UTC. This value ranges from Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000.1626009506
QMilliseconds since the beginning of the epoch starting January 1, 1970 at 00:00:00 UTC. This value ranges from Long.MIN_VALUE to Long.MAX_VALUE.1626009506640

Date and Time

There are also convenience conversions that combine common date and time formats into a single format specifier. The complete list of these conversions is contained in the table below:

r%tI:%tM:%tS %Tp09:18:26 AM
F%tY-%tm-%td (i.e., ISO 8601 complete date)2021-07-11
c%ta %tb %td %tT %tZ %tYSun Jul 11 09:18:26 EDT 2021


Standard conversions can be straightforward since the conversion simply follows the % prefix, but date and time conversions can often be confusing. Instead of using the conversion after the % prefix, we must first append the date/time prefix, t or T, and then append the desired date or time conversion. In essence, when dealing with date and time formatting, the prefix can be thought of as %t or %T, rather than just %.

For example, if we want to format the number of seconds since the January 1, 1970 epoch, we must use the %ts format specifier and supply a date-based value (such as a long or Date):Java1

Date date1 = new Date();


long date2 = 1626009506640L;



String formattedString1 = String.format("%ts", date1);


String formattedString2 = String.format("%ts", date2);

This snippet results in the following values:

  • formattedString11626013108
  • formattedString21626009506

Similarly, we can also the 24-hour time — hours and minutes — using the following snippet:Java1

Date date = new Date();



String formattedString = String.format("%1$tH %1$tM", date);

This results in the following value for formattedString:Plain Text1

10 32

Note that we have to provide the 1$ index to the format specifiers to provide only one argument (as we saw previously). Otherwise, the JVM would assume that our second format specifier (%tM) corresponds to the second argument in the argument — which does not exist. While we provided the index to both format specifiers, we are only required to prepend it to format specifiers other than the first one (in our case, the second format specifier). For example, the following snippet is equivalent to the String.format expression from our previous example:Java1

String formattedString = String.format("%tH %1$tM", date);

While date and time conversions can seem cryptic and complicated compared to the standard conversions, they follow nearly the same structure, but instead of using % only as a prefix, we use %t or %T.

Formatting Non-Argument Specifiers

There also exist specifiers that do not take arguments, such as % and n, which print the percent character and a platform-specific new line, respectively. These non-argument specifiers have the following format:Plain Text1


The <flags> and <width> components have the same meaning as in the general and date case, while the <conversion> is reserved for the set of conversions that do not require (or accept) accompanying arguments. The complete list of these conversions is contained in the following table:

%The literal % character. I.e., String.format("%%") results in the string %.%
nPlatform-specific line separator (new-line). This conversion should be favored over using \n or or \r\n.Hello\nWorld

 As an example, we can store a String with a percent character and a new line using the following:Java1

String formattedString = String.format("100%% complete.%nDone.");

This snippet results in the following String being stored in formattedString (for Linux):Plain Text1

100% complete.\nDone.

Note that we do not provide an argument list to the String.format call. Any arguments provided in an argument list for a non-argument specifier are ignored. For example, the following snippet will produce the same result — the String 100% complete.\nDone. being stored to formattedString — as our previous example:Java1

String formattedString = String.format("100%% complete.%nDone.", "foo");

Additional Java String Formatting

Apart from conversions, we can also format Java Strings using flags, widths, and precisions.


Flags act as general formatters that apply to various subsets of categories (e.g. general, date, and non-argument). It is important to note that not all flags work with every type of argument. The following table enumerates all of the available flags, as well which argument types to which they apply (where Y denotes that a flag applies to all conversions in that subcategory):

-Left-justifies the results. Note that this flag must be accompanied by a width or a MissingFormatWidthException will be thrown.YYYYY
#Uses an alternative form. The specific alternate form depends on the conversion. For more information, see the Details section of Formatter class.YFor ox, and XY
+Always includes a sign.For dox, and X applied to BigDecimal; or d applied to byteByteshortShortintIntegerlong, and LongY
(space)Add leading space for positive values.For dox, and X applied to BigDecimal; or d applied to byteByteshortShortintIntegerlong, and LongY
0Zero-pads the result.YY
,Includes locale-specific grouping separators.For d conversionFor eEfg, and G conversion
(Encloses negative numbers in parenthesis.For dox, and X applied to BigDecimal; or d applied to byteByteshortShortintIntegerlong, and LongFor eEfg, and G conversion

It is important to note that more than one flag can be used at a time. For example, if we want to left-justify a numeric value while also including the sign, we can use the -+ flag (and a width, which we will specify as 10 for this example):Java1

String formattedString = String.format("%-+10d", 1745);

This results in the following being stored to formattedString (quotes added to emphasize the trailing spaces due to padding):Plain Text1

"+1745     "


The width of a format specifier defines its minimum size. If the supplied argument does not contain enough characters after formatting, spaces are used to fulfill the minimum width. For example, if the format specifier %10s is used and the String name is supplied, six trailing spaces will be added to pad the result to fulfill the minimum width of 10 characters. If the - flag is used to left-justify the result, the whitespace is prefixed to the beginning of the resulting String. For example:Java1

String formattedString1 = String.format("%10s", "name");    // Result: "name      "


String formattedString2 = String.format("%-10s", "name");   // Result: "      name"


The meaning of precision depends on the conversion:

  • General: Maximum number of characters displayed; if the supplied argument results in more characters than the supplied precision, the resulting string is truncated to the correct number of characters. For example, supplying Justin as an argument to the format specifier %.1s result in J.
  • Floating point (eE, and f): Number of digits after the decimal place. For example, a format specifier of %.1f with an argument of 1.2345 results in 1.2.
  • Floating point (gGa and A): Number of digits in the magnitude after rounding.
  • Character, Integer, Date, Percent Symbol & Line Separator: Must not be specified. Specifying precision results in a IllegalFormatPrecisionException.

More Information

For more detailed information about C-style String formatting in Java, see the following:

  • Formatter class documentation: Most of the information about C-style formatting can be found in the Javadocs for the Formatter class. In particular, the Details section provides implementation and behavioral details about each of the conversions mentioned in this article, as well as supplemental information about the various flags, widths, and precisions available when formatting Strings in Java using the C-style technique.
  • Formatting: A tutorial provided by Oracle on the basics of formatting String using the C-style.
  • Formatting Numeric Print Output: A tutorial provided by Oracle on formatting numeric values using the C-style.


Formatting Strings in Java can be tedious and difficult to remember. While there are a nearly unlimited number of combinations available to us, it is important that we understand the basics of how these various conversions, flags, widths, and precisions can be combined to form Strings. It is almost inevitable that we will have to look up specific conversions or flags — even after years of practice — but it is essential that we have a foundational knowledge of how Java Strings are formatted and how we can combine the various components of the C-style methods to create the Strings we desire.