Study guide

2.2 Switch Statements and Switch Expressions

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

2.2 Switch Statements and Switch Expressions

Key Takeaways Explain This Topic AI

Selector expressions in standard Java SE 17 accept byte, short, char, int, their respective boxed wrappers, String, and enums; long, float, double, and boolean are disallowed.

Case labels must be compile-time constants such as literals, constant expressions, or final variables initialized at declaration; non-constant variables cause compilation failures.

Classic switch statements using colon (:) syntax exhibit fall-through across cases unless terminated by break or return, and share a single variable scope across all cases.

Arrow labels (->) never fall through and allow comma-separated case values; switch expressions return values directly or with the yield keyword in multi-statement blocks, while colon-form switch expressions still fall through.

Switch expressions that produce a value must be exhaustive; a default branch is required unless all constants of an enum selector are explicitly handled.

Last updated: October 2026

2.2 Switch Statements and Switch Expressions

The switch construct in Java provides multi-way branching based on the value of a selector expression. Since switch expressions were standardized in Java 14 (JEP 361), the language supports two label forms (colon case X: and arrow case X -> ) and two uses of switch (as a statement that performs actions, or as an expression that produces a value with yield where needed). Java SE 17 LTS includes both. The 1Z0-829 exam tests deep knowledge of permitted selector types, compile-time constant requirements for case labels, fall-through rules, variable scoping, and exhaustiveness checks.

Note

Pattern matching for switch (e.g., case Integer i -> ... ) was a preview feature in Java 17 (JEP 406) and was not finalized until Java 21 (JEP 441). The Java SE 17 Developer exam (1Z0-829) covers standard, non-preview language features: classic switch statements and standard switch expressions.

Permitted and Disallowed Selector Types

Java restricts the data types that may serve as the selector expression in a switch construct.

  • Permitted Selector Types Disallowed Selector Types
  • byte , short , char , int (primitives) long (and java.lang.Long )
  • Byte , Short , Character , Integer (wrappers) float (and java.lang.Float )
  • java.lang.String double (and java.lang.Double )
  • enum types boolean (and java.lang.Boolean )
  • Arbitrary Object references
  • The null Selector Trap

If the selector expression evaluates to null at runtime—whether it is a boxed wrapper (such as Integer ) or a String reference—Java immediately throws a NullPointerException upon entering the switch construct: String input = null;

// Throws NullPointerException before checking any case or default!

switch (input) {

case "admin":

  • System.out.println("Administrator");
  • break;

default:

  • System.out.println("Guest");
  • }

The switch does not fall into the default branch when the selector is null . A runtime exception is thrown immediately.

Classic Switch Statements: Constant Expressions & Fall-Through

In classic switch statements, each case label must be a compile-time constant expression whose type is assignment-compatible with the switch selector:

Literals : e.g., case 1: , case "READY": .

Compile-Time Constant Variables : A variable declared final and initialized at the point of declaration with a constant expression (e.g., final int BASE = 10; ).

Enum Constants : When switching on an enum type, the case label must specify the unqualified name of the enum constant (e.g., case NORTH: , not case Direction.NORTH: ). final int MAX = 100;

final int UNASSIGNED;

UNASSIGNED = 50; // Blank final: NOT a compile-time constant!

int dynamicValue = 20;

  • int test = 10;
  • switch (test) {
  • case MAX: // Legal: compile-time constant
  • break;
  • // case UNASSIGNED: // COMPILE ERROR: constant expression required
  • // break;
  • // case dynamicValue: // COMPILE ERROR: constant expression required
  • // break;
  • }

Furthermore, the value of a case label must fit within the range of the selector's primitive type. If switching on a byte (range -128 to 127 ), a case label of case 130: causes a compilation error: incompatible types: possible lossy conversion from int to byte .

Fall-Through Mechanics

Classic switch branches delimited with a colon ( : ) exhibit fall-through. If a matching case does not terminate with a break , return , throw , or yield , execution flows sequentially into the statements of subsequent cases regardless of whether those case labels match the selector: int day = 2;

switch (day) {

case 1:

System.out.print("Mon ");

case 2:

System.out.print("Tue ");

case 3:

  • System.out.print("Wed ");
  • break;

default:

  • System.out.print("Other ");
  • }
  • // Outputs: "Tue Wed "

If day is 2, execution jumps directly to case 2: . It prints "Tue " , falls through to case 3: , prints "Wed " , and then encounters break , which terminates the switch.

Variable Scoping Across Switch Cases

A classic switch block forms a single scope for local variables between the opening { and closing } . A variable declared inside one case remains in scope for all subsequent case and default blocks: int selector = 1;

switch (selector) {

case 1:

  • int value = 42;
  • System.out.println(value);
  • break;

case 2:

// COMPILE ERROR: variable value is already defined in scope!

  • int value = 99;
  • System.out.println(value);
  • break;

default:

// value is still in scope here, but might NOT be initialized!

  • // System.out.println(value); // COMPILE ERROR if used before initialization
  • }

To isolate variable declarations between cases, each branch must be enclosed in its own block using curly braces: switch (selector) {

  • case 1 -> { int value = 42; System.out.println(value); }
  • case 2 -> { int value = 99; System.out.println(value); } // Legal in isolated blocks
  • }
  • Modern Switch Expressions (Java 14 / Java 17 LTS)

Java SE 17 provides modern switch expressions using the arrow ( -> ) syntax. Switch expressions resolve many long-standing design traps of classic switches:

No Fall-Through : Exactly the code on the right-hand side of the matching arrow executes, so no break is needed. (A break is still legal inside an arrow block of a switch statement , but a switch expression cannot be exited with break .)

Multiple Comma-Separated Labels : Multiple matching values can share a single branch separated by commas ( case 1, 2, 3 -> ).

Value Production : A switch expression can return a value directly to a variable assignment or method call. int month = 4;

  • int days = switch (month) {
  • case 1, 3, 5, 7, 8, 10, 12 -> 31;
  • case 4, 6, 9, 11 -> 30;
  • case 2 -> 28;
  • default -> 0;
  • };
  • The yield Keyword

When a branch in a switch expression requires multiple statements, the logic is enclosed in a block { ... } . To return a value from this block, Java uses the contextual keyword yield : int code = 3;

  • String description = switch (code) {
  • case 1 -> "Low Priority";
  • case 2, 3 -> {
  • String prefix = "Urgent: Level ";
  • yield prefix + code; // Returns value from block
  • }
  • default -> "Standard";
  • };
  • Critical Rules Governing yield

yield takes an argument and passes it as the result of the enclosing switch expression.

return exits the entire enclosing method, whereas yield only exits the switch expression.

You cannot use yield inside a classic switch statement that does not return a value.

You cannot mix arrow ( -> ) syntax and colon ( : ) syntax within the same switch construct. A switch must use exclusively arrows or exclusively colons. // COMPILE ERROR: cannot mix ':' and '->' in the same switch

  • int invalid = switch (code) {
  • case 1 -> 10;
  • case 2: yield 20;
  • default -> 0;
  • };
  • Exhaustive Matching Requirements

When a switch is used as an expression (evaluating to a value), the compiler strictly requires exhaustive coverage of all possible input values:

Primitives and Strings : Because int , char , String , etc., have effectively boundless or large value domains, a default branch is mandatory . Omitting default results in a compile-time error: the switch expression does not cover all possible input values .

Enum Types : If the switch selector is an enum , a default case is optional if and only if every enum constant defined in the enumeration is explicitly handled by a case label: enum Status { PENDING, ACTIVE, REJECTED }

Status current = Status.ACTIVE;

// Legal without 'default' because all 3 constants are covered:

  • int statusCode = switch (current) {
  • case PENDING -> 1;
  • case ACTIVE -> 2;
  • case REJECTED -> 3;
  • };

If a new enum constant (e.g., CANCELLED ) is subsequently added to the enum definition, the switch expression will fail compilation until the new constant is handled or a default branch is provided.

When a switch is used as a statement (performing actions without producing a value), exhaustiveness is not required.

Note

Two independent choices. Fall-through depends on the label form , and exhaustiveness depends on whether the switch is an expression . A switch expression may use colon labels with yield (and then falls through like a classic switch), and a switch statement may use arrow labels (no fall-through, no exhaustiveness check). The comparison table below contrasts the two most common combinations: a colon-form statement and an arrow-form expression.

Comparison: Classic Switch vs. Switch Expression

  • Feature Classic Switch Statement Modern Switch Expression
  • Primary Syntax Colons ( case VALUE: ) Arrows ( case VALUE -> )
  • Fall-Through Yes (automatic without break ) No (never falls through)

Multiple Labels per Branch Stacked ( case 1: case 2: ) or comma-separated ( case 1, 2: ) Comma-separated ( case 1, 2 -> )

  • Value Production Cannot evaluate to a value Returns a value via direct expression or yield
  • Multi-line Block Termination break; yield value;
  • Exhaustiveness Not enforced by compiler Mandatory (must cover all values or have default )
  • Variable Scoping Shared across entire switch block Isolated to respective block {} branches