Java NumberFormat Class: Formatting Numbers, Currency and Percentages - Quiz

Total: 14 questions

1. 

What is the NumberFormat class in Java and how do you get an instance of it?

java.text.NumberFormat is an abstract class that formats numbers, currency amounts and percentages according to the rules of a locale (decimal and grouping separators, currency symbol, number of fraction digits) and parses such strings back into numbers with parse.

You do not create it with new; you get it from factory methods:

  • getInstance() / getNumberInstance(): a plain number, 0 to 3 fraction digits by default;
  • getIntegerInstance(): an integer, the fraction is rounded;
  • getCurrencyInstance(): a money amount with a currency symbol;
  • getPercentInstance(): a percentage, the number is multiplied by 100;
  • getCompactNumberInstance(locale, style): short notation such as 1K, 2M (Java 12+).

Each method has a variant without arguments (the default locale, taken from the OS settings) and one with a Locale parameter. When the result matters, pass the locale explicitly: on a server it can differ from the developer machine.

2. 

How does getCurrencyInstance determine the currency, and why can the ¤ sign appear in the output?

The currency is determined by the country of the locale, not by the language. So getCurrencyInstance(Locale.FRANCE) prints 123,46 €, while for a locale without a country (Locale.FRENCH, Locale.GERMAN) Java cannot pick a currency and prints the generic currency sign ¤ (code XXX): 123,46 ¤. No exception is thrown.

There are two fixes:

  • pass a locale with a country: Locale.FRANCE, Locale.GERMANY;
  • set the currency explicitly: nf.setCurrency(Currency.getInstance("EUR")).

setCurrency changes only the currency; separators and symbol position stay the ones from the formatter locale: a Locale.US formatter with EUR prints €1,234.50. The number of fraction digits also comes from the currency (2 for USD, EUR, RUB).

3. 

Which NumberFormat methods control the number of digits, and what non-obvious effects do they have?

  • setMaximumFractionDigits(n): the maximum number of digits after the separator, the last one is rounded. For getInstance() the defaults are a maximum of 3 and a minimum of 0.
  • setMinimumFractionDigits(n): the minimum number of digits after the separator, missing ones are filled with zeros: 123.4567800.
  • setMaximumIntegerDigits(n): the maximum number of integer digits. The leading digits are silently dropped: with n = 2 the number 123.45 is printed as 23.45, with no exception.
  • setMinimumIntegerDigits(n): leading zeros. They count as ordinary digits, so the group separator is placed between them too: 00,123.
  • setGroupingUsed(false) turns the group separator off; this is how you get numbers like 00123.

If a new maximum is less than the current minimum, the minimum is lowered too, and vice versa.

4. 

How does NumberFormat round numbers, and how do you format money amounts without losing precision?

The default is RoundingMode.HALF_EVEN (banker's rounding): an exact half is rounded to the nearest even digit, so 2.5 gives 2 and 3.5 gives 4. You change the rule with setRoundingMode, for example setRoundingMode(RoundingMode.HALF_UP).

What gets rounded is the exact binary value of the double, not the literal in the code: 0.135 is stored as 0.13500000000000000888…, so with two fraction digits the result is 0.14 even in HALF_EVEN mode.

For money, pass a BigDecimal to format: it is rounded with the formatter RoundingMode without an intermediate double. To parse back, cast the formatter to DecimalFormat and call setParseBigDecimal(true); then parse returns a BigDecimal.

5. 

How does NumberFormat.parse work, and how does its behavior differ from Integer.parseInt?

parse(String) parses a string using the rules of the formatter locale and returns a Number: a Long if the result is an integer that fits into long, otherwise a Double.

  • The number is read from the start of the string and the tail is silently ignored: parse("12abc") returns 12.
  • If the string does not start with a number ("abc"), the checked java.text.ParseException is thrown. Since Java 23, strict mode setStrict(true) makes "12abc" an error too.
  • setParseIntegerOnly(true) stops parsing at the decimal separator: "1234,567" in de_DE gives 1234.
  • The same string means different numbers in different locales: in US the comma is a grouping separator, so "1234,567" becomes 1234567.

Integer.parseInt and Double.parseDouble ignore the locale and throw the unchecked NumberFormatException on "12abc". NumberFormat itself never throws that exception.

6. 

How does DecimalFormat differ from NumberFormat, and when should you use it?

DecimalFormat is a subclass of NumberFormat that formats a number by an explicit pattern. Usually NumberFormat.getInstance() returns exactly a DecimalFormat configured with the locale pattern.

Pattern symbols: 0 is a required digit, # an optional digit, , the place of the group separator, . the decimal separator. For example, new DecimalFormat("#,##0.00", symbols).format(1234.5) gives 1,234.50, and the pattern "000.##" gives 005.1 for 5.1.

Which characters actually replace , and . is decided by DecimalFormatSymbols; without it the symbols of the default locale are used. A DecimalFormat with explicit DecimalFormatSymbols is the right choice when the format is fixed in advance (a report, CSV, a document number) and must not depend on the user language.

7. 

Can one NumberFormat object be shared between threads?

No. NumberFormat and DecimalFormat are not thread-safe: they keep mutable state inside, and calling format or parse from several threads at once can mix up the results without any exception.

Ways to solve it:

  • create the formatter inside the method (every call to a factory method returns a new object);
  • keep a separate instance per thread in a ThreadLocal;
  • synchronize access to a shared formatter.

So a configured formatter in a static final field is safe only in single-threaded code.

8. 

What is the NumberFormat class in Java and how do you get an instance of it?

java.text.NumberFormat is an abstract class that formats numbers, currency amounts and percentages according to the rules of a locale (decimal and grouping separators, currency symbol, number of fraction digits) and parses such strings back into numbers with parse.

You do not create it with new; you get it from factory methods:

  • getInstance() / getNumberInstance(): a plain number, 0 to 3 fraction digits by default;
  • getIntegerInstance(): an integer, the fraction is rounded;
  • getCurrencyInstance(): a money amount with a currency symbol;
  • getPercentInstance(): a percentage, the number is multiplied by 100;
  • getCompactNumberInstance(locale, style): short notation such as 1K, 2M (Java 12+).

Each method has a variant without arguments (the default locale, taken from the OS settings) and one with a Locale parameter. When the result matters, pass the locale explicitly: on a server it can differ from the developer machine.

9. 

How does getCurrencyInstance determine the currency, and why can the ¤ sign appear in the output?

The currency is determined by the country of the locale, not by the language. So getCurrencyInstance(Locale.FRANCE) prints 123,46 €, while for a locale without a country (Locale.FRENCH, Locale.GERMAN) Java cannot pick a currency and prints the generic currency sign ¤ (code XXX): 123,46 ¤. No exception is thrown.

There are two fixes:

  • pass a locale with a country: Locale.FRANCE, Locale.GERMANY;
  • set the currency explicitly: nf.setCurrency(Currency.getInstance("EUR")).

setCurrency changes only the currency; separators and symbol position stay the ones from the formatter locale: a Locale.US formatter with EUR prints €1,234.50. The number of fraction digits also comes from the currency (2 for USD, EUR, RUB).

10. 

Which NumberFormat methods control the number of digits, and what non-obvious effects do they have?

  • setMaximumFractionDigits(n): the maximum number of digits after the separator, the last one is rounded. For getInstance() the defaults are a maximum of 3 and a minimum of 0.
  • setMinimumFractionDigits(n): the minimum number of digits after the separator, missing ones are filled with zeros: 123.4567800.
  • setMaximumIntegerDigits(n): the maximum number of integer digits. The leading digits are silently dropped: with n = 2 the number 123.45 is printed as 23.45, with no exception.
  • setMinimumIntegerDigits(n): leading zeros. They count as ordinary digits, so the group separator is placed between them too: 00,123.
  • setGroupingUsed(false) turns the group separator off; this is how you get numbers like 00123.

If a new maximum is less than the current minimum, the minimum is lowered too, and vice versa.

11. 

How does NumberFormat round numbers, and how do you format money amounts without losing precision?

The default is RoundingMode.HALF_EVEN (banker's rounding): an exact half is rounded to the nearest even digit, so 2.5 gives 2 and 3.5 gives 4. You change the rule with setRoundingMode, for example setRoundingMode(RoundingMode.HALF_UP).

What gets rounded is the exact binary value of the double, not the literal in the code: 0.135 is stored as 0.13500000000000000888…, so with two fraction digits the result is 0.14 even in HALF_EVEN mode.

For money, pass a BigDecimal to format: it is rounded with the formatter RoundingMode without an intermediate double. To parse back, cast the formatter to DecimalFormat and call setParseBigDecimal(true); then parse returns a BigDecimal.

12. 

How does NumberFormat.parse work, and how does its behavior differ from Integer.parseInt?

parse(String) parses a string using the rules of the formatter locale and returns a Number: a Long if the result is an integer that fits into long, otherwise a Double.

  • The number is read from the start of the string and the tail is silently ignored: parse("12abc") returns 12.
  • If the string does not start with a number ("abc"), the checked java.text.ParseException is thrown. Since Java 23, strict mode setStrict(true) makes "12abc" an error too.
  • setParseIntegerOnly(true) stops parsing at the decimal separator: "1234,567" in de_DE gives 1234.
  • The same string means different numbers in different locales: in US the comma is a grouping separator, so "1234,567" becomes 1234567.

Integer.parseInt and Double.parseDouble ignore the locale and throw the unchecked NumberFormatException on "12abc". NumberFormat itself never throws that exception.

13. 

How does DecimalFormat differ from NumberFormat, and when should you use it?

DecimalFormat is a subclass of NumberFormat that formats a number by an explicit pattern. Usually NumberFormat.getInstance() returns exactly a DecimalFormat configured with the locale pattern.

Pattern symbols: 0 is a required digit, # an optional digit, , the place of the group separator, . the decimal separator. For example, new DecimalFormat("#,##0.00", symbols).format(1234.5) gives 1,234.50, and the pattern "000.##" gives 005.1 for 5.1.

Which characters actually replace , and . is decided by DecimalFormatSymbols; without it the symbols of the default locale are used. A DecimalFormat with explicit DecimalFormatSymbols is the right choice when the format is fixed in advance (a report, CSV, a document number) and must not depend on the user language.

14. 

Can one NumberFormat object be shared between threads?

No. NumberFormat and DecimalFormat are not thread-safe: they keep mutable state inside, and calling format or parse from several threads at once can mix up the results without any exception.

Ways to solve it:

  • create the formatter inside the method (every call to a factory method returns a new object);
  • keep a separate instance per thread in a ThreadLocal;
  • synchronize access to a shared formatter.

So a configured formatter in a static final field is safe only in single-threaded code.

Page 1 of 1