Study guide
12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages
Oracle Java SE 21 Developer 1Z0-830
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Java Basics, Primitive Types, and Operators
1.1 Program Structure, Primitives, Literals, Scopes, and var 1.2 Operators, Precedence, and Type Casting 1.3 Strings, Text Blocks, and StringBuilder 1.4 Date and Time APIs (java.time)
2 Chapter 2: Controlling Program Flow & Pattern Matching
2.1 Conditional Logic: if-else and Ternary Operators 2.2 Switch Statements and Switch Expressions 2.3 Pattern Matching for switch and instanceof 2.4 Loops and Branching Statements
3 Chapter 3: Object-Oriented Design & Class Architecture
3.1 Class Declarations, Constructors, and Object Lifecycle 3.2 Encapsulation and Access Modifiers 3.3 Inheritance, Method Overriding, and Polymorphism 3.4 Abstract Classes and Interfaces 3.5 Nested, Inner, Local, and Anonymous Classes 3.6 Method Overloading, Overload Resolution, and Varargs
4 Chapter 4: Modern Java Data Modeling: Records, Enums, and Sealed Types
4.1 Java Records, Canonical/Compact Constructors, and Component Accessors 4.2 Java Enums: Fields, Methods, and Abstract Constant-Specific Behavior 4.3 Sealed Classes and Interfaces: permits, final, non-sealed, and Hierarchy Architecture 4.4 Advanced Record Patterns (JEP 440), Nested Deconstruction, and Sealed Hierarchy Exhaustiveness
5 Chapter 5: Exception Handling and Annotations
5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions 5.2 Exception Handling: try, catch, finally, and Multi-Catch 5.3 Resource Management: try-with-resources and AutoCloseable 5.4 Custom Exceptions and Best Practices 5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types
6 Chapter 6: Generics and the Collections Framework
6.1 Generics Fundamentals and Generic Methods 6.2 Wildcards, Type Bounds, and Type Erasure 6.3 Core Collections: Lists, Sets, Queues, and Maps 6.4 Sequenced Collections in Java 21 6.5 Sorting, Comparable, and Comparator 6.6 Arrays: Creation, Multidimensional Forms, and the Arrays Utility Class
7 Chapter 7: Functional Programming & Lambda Expressions
7.1 Built-in Functional Interfaces in java.util.function 7.2 Lambda Expressions, Variable Scopes, and Effectively Final 7.3 Method References: Four Varieties and Edge Cases 7.4 Working with java.util.Optional
8 Chapter 8: The Stream API and Data Pipelines
8.1 Stream Creation and Intermediate Operations 8.2 Terminal Operations and Reductions 8.3 Advanced Collectors, Grouping, Partitioning, and Teeing 8.4 Primitive Streams and Parallel Stream Processing
9 Chapter 9: Concurrency, Multithreading, and Virtual Threads
9.1 Thread Fundamentals, Creation, and Lifecycle 9.2 Thread Safety, Synchronization, and the Java Memory Model 9.3 Concurrency Utilities: ExecutorService, Callable, and Future 9.4 Concurrent Collections, Atomics, and Explicit Locks 9.5 Virtual Threads in Java 21 (Carrier Threads, Pinning, and Best Practices)
10 Chapter 10: The Java Platform Module System (JPMS)
10.1 Module Architecture and module-info.java Declarations 10.2 Module Directives: requires, exports, opens, provides, and uses 10.3 Modular Compilation, Packaging, and CLI Execution 10.4 Module Types, Migration Strategies, ServiceLoader SPI, and JLink
11 Chapter 11: Java I/O, NIO.2, and Object Serialization
11.1 Classic Java I/O Streams and Readers/Writers 11.2 Java NIO.2 File API: Path, Paths, and File System Operations 11.3 Advanced NIO.2 Stream Operations and Directory Traversal 11.4 Java Object Serialization and Record Serialization
12 Chapter 12: Implementing Localization and Internationalization
12.1 Locales and Resource Bundles 12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages
12.2 Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages
Key Takeaways Explain This Topic AI
NumberFormat.getPercentInstance multiplies its argument by 100 and defaults to zero fraction digits, so 0.25 renders as 25% while 25 renders as 2,500%.
NumberFormat.parse returns a Number and throws java.text.ParseException, which is checked, whereas LocalDate.parse throws DateTimeParseException, which is unchecked.
In a DecimalFormat pattern the 0 symbol pads an absent digit with a zero while the # symbol omits the position entirely, and the default rounding mode across NumberFormat is HALF_EVEN.
DateTimeFormatter is immutable and thread-safe, so withLocale returns a new formatter, and the LONG and FULL time styles require a zone or they throw DateTimeException on a LocalTime or LocalDateTime.
MessageFormat treats a single quote as the start of a literal region, so an apostrophe must be doubled or the remaining placeholders are emitted as literal text.
Last updated: September 2026
Formatting and Parsing Numbers, Currencies, Dates, Times, and Messages
The second half of Oracle's localization objective is "Parse and format messages, dates, times, and numbers, including currency and percentage values." Java splits this work across three unrelated APIs, and the exam tests the seams between them - especially which exceptions are checked and which are not.
- API Package Handles Parse failure throws
- NumberFormat / DecimalFormat java.text Numbers, currency, percentages ParseException ( checked )
- DateTimeFormatter java.time.format Dates, times, zones DateTimeParseException ( unchecked )
- MessageFormat java.text Composite messages with placeholders ParseException ( checked )
- 1. NumberFormat : Numbers, Currency, and Percentages
NumberFormat is obtained from static factories, never constructed directly. Every factory has a no-argument form that uses the default FORMAT locale and an overload that takes an explicit Locale . double amount = 1234.5;
- NumberFormat.getInstance(Locale.US).format(amount); // 1,234.5
- NumberFormat.getInstance(Locale.GERMANY).format(amount); // 1.234,5
- NumberFormat.getCurrencyInstance(Locale.US).format(amount); // $1,234.50
- NumberFormat.getIntegerInstance(Locale.US).format(amount); // 1,235 (rounds)
- NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT)
- .format(1_200_000); // 1M
Germany swaps the roles of . and , and moves the currency symbol after the amount, which is exactly the sort of contrast the exam uses to check that you understand the locale is doing the work.
- Percentages Multiply by 100 NumberFormat pct = NumberFormat.getPercentInstance(Locale.US);
- pct.format(0.25); // 25%
- pct.format(0.256); // 26% <- percent instances default to 0 fraction digits
- pct.setMaximumFractionDigits(1);
- pct.format(0.256); // 25.6%
Forgetting the implicit multiplication is the classic error: formatting 25 with a percent instance yields 2,500% , not 25% .
- Parsing Returns a Number and Throws a Checked Exception NumberFormat nf = NumberFormat.getInstance(Locale.US);
- try {
- Number n = nf.parse("1,234.5"); // Double 1234.5
- Number i = nf.parse("1,234"); // Long 1234 - integral input yields a Long
- } catch (ParseException e) { // java.text.ParseException is CHECKED
- // must be caught or declared
- }
parse also stops at the first unparseable character rather than failing: nf.parse("12abc") quietly returns 12 .
DecimalFormat Patterns
DecimalFormat is the NumberFormat subclass that accepts an explicit pattern. Two symbols carry all the weight:
- Symbol Meaning
- 0 A digit; always shown , padded with a zero if absent
- # A digit; omitted when the position has no significant digit
- , Grouping separator (the actual character comes from the locale)
- . Decimal separator (likewise locale-dependent)
% Multiply by 100 and append the locale's percent sign new DecimalFormat("#,###.##").format(3456.789); // 3,456.79 (HALF_EVEN rounding)
- new DecimalFormat("000.000").format(3.5); // 003.500
- new DecimalFormat("#.##").format(0.5); // 0.5 -> note the leading digit survives
- new DecimalFormat("#.00").format(9); // 9.00
The default rounding mode across NumberFormat is RoundingMode.HALF_EVEN , not HALF_UP .
2. DateTimeFormatter : Localized Dates and Times
DateTimeFormatter lives in java.time.format and is immutable and thread-safe - the opposite of the legacy SimpleDateFormat . Because it is immutable, withLocale returns a new formatter: DateTimeFormatter base = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
- base.withLocale(Locale.FRANCE); // return value DISCARDED - a common bug
- DateTimeFormatter fr = base.withLocale(Locale.FRANCE); // correct
- Localized styles let the locale choose the pattern for you: LocalDate date = LocalDate.of(2026, 9, 2);
- DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.US).format(date);
- // 9/2/26
- DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.FRANCE).format(date);
- // 02/09/2026
- DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.FRANCE).format(date);
- // 02 septembre 2026
Caution
The LONG and FULL time styles include a time-zone field. Applying ofLocalizedTime(FormatStyle.FULL) to a LocalTime or ofLocalizedDateTime(FormatStyle.FULL) to a LocalDateTime throws a DateTimeException at run time, because those temporals carry no zone. Use a ZonedDateTime , or drop to SHORT / MEDIUM .
Formatting reads equally well from either side, and parsing is a static method on the target type: String text = fmt.format(date); // formatter-first
String same = date.format(fmt); // temporal-first - identical result
- LocalDate back = LocalDate.parse("02/09/2026",
- DateTimeFormatter.ofPattern("dd/MM/yyyy"));
- // A mismatch throws DateTimeParseException - a RuntimeException, so NO catch is required
That asymmetry is worth committing to memory: java.text parsing forces a try / catch , java.time parsing does not.
3. MessageFormat : Assembling Localized Sentences
Concatenating translated fragments produces broken word order in other languages. MessageFormat instead uses numbered placeholders , so a translation can reorder them freely. String pattern = "{0}, you have {1} new messages on {2}.";
MessageFormat.format(pattern, "Duke", 3, LocalDate.of(2026, 9, 2));
// Duke, you have 3 new messages on 2026-09-02.
Placeholders may carry a type and a style, which is how currency, percentage, and date formatting reach into a message: String pattern = "{0} spent {1,number,currency} ({2,number,percent} of budget).";
- MessageFormat mf = new MessageFormat(pattern, Locale.US);
- mf.format(new Object[] { "Duke", 1234.5, 0.42 });
// Duke spent $1,234.50 (42% of budget).
Supported forms include {n,number,integer|currency|percent} , {n,date,short|medium|long|full} , {n,time,...} , and {n,choice,...} .
Important
Inside a MessageFormat pattern a single quote starts a quoted literal . To emit one apostrophe you must double it, and text wrapped in single quotes is passed through untouched: "It's {0}" -> It s {0} placeholder is consumed as literal text
- "It''s {0}" -> It's Duke (correct)
- "'{0}' is {1}" -> {0} is Duke (the first placeholder is literal)
The lightweight alternative for simple cases is String.format , which also accepts a locale: String.format(Locale.US, "%,.2f", 1234.5); // 1,234.50
- String.format(Locale.GERMANY, "%,.2f", 1234.5); // 1.234,50
- 4. Exam Checklist
Percent instances multiply by 100 and default to zero fraction digits.
NumberFormat.parse returns a Number (often Long for integral text) and throws the checked ParseException .
LocalDate.parse throws the unchecked DateTimeParseException .
DateTimeFormatter is immutable: assign the result of withLocale .
FULL / LONG time styles need a zone; a LocalTime or LocalDateTime throws DateTimeException .
In DecimalFormat , 0 pads and # disappears.
Default rounding is HALF_EVEN .
In MessageFormat , write '' for a literal apostrophe.