Skip to content
Java control flow 7 min read

switch Statement

When you need to compare a single variable against a list of specific values, switch is often cleaner and more readable than a long chain of else if blocks. Java has evolved the switch statement significantly over the years — from its classic C-style roots to the modern expression form introduced in Java 14.

The Classic switch Statement

The traditional switch evaluates an expression and jumps to the matching case label. If none match, it falls through to default.

int day = 3;

switch (day) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    case 4:
        System.out.println("Thursday");
        break;
    case 5:
        System.out.println("Friday");
        break;
    default:
        System.out.println("Weekend");
}

Output:

Wednesday

Note: The break at the end of each case is mandatory if you want to stop after a match. Without it, Java falls through to the next case — intentionally or not. We’ll cover fall-through shortly.

What Types Can You Switch On?

Java’s switch is more restrictive than if-else — the selector expression must be one of:

Allowed TypesSince
byte, short, int, charJava 1.0
Byte, Short, Integer, Character (wrapper types)Java 5
StringJava 7
enum constantsJava 5
Pattern types (Object, interfaces, etc.)Java 21 (preview → final)

Warning: You cannot switch on long, float, double, or boolean in the classic form. Attempting to do so causes a compile-time error.

The default Case

default is the catch-all. It runs when no case value matches the expression. It’s optional, but omitting it can cause silent no-ops that are hard to debug.

String fruit = "Mango";

switch (fruit) {
    case "Apple":
        System.out.println("Apple selected");
        break;
    case "Banana":
        System.out.println("Banana selected");
        break;
    default:
        System.out.println("Unknown fruit: " + fruit);
}

Output:

Unknown fruit: Mango

Tip: Always include a default case, even if it just logs a warning or throws an exception. It makes your switch robust against unexpected input.

The default case doesn’t have to be last — it can appear anywhere in the block, though putting it last is the conventional style.

Fall-Through Behavior

Fall-through is one of switch’s most misunderstood features. When you omit break, execution continues into the next case — even if that case’s label doesn’t match.

int level = 2;

switch (level) {
    case 1:
        System.out.println("Level 1");
    case 2:
        System.out.println("Level 2");  // matched here
    case 3:
        System.out.println("Level 3");  // falls through!
        break;
    case 4:
        System.out.println("Level 4");
}

Output:

Level 2
Level 3

This is a common source of bugs. However, fall-through can be intentional — the classic use case is grouping multiple values together:

int month = 4; // April
int year = 2024;
int daysInMonth;

switch (month) {
    case 1: case 3: case 5:
    case 7: case 8: case 10: case 12:
        daysInMonth = 31;
        break;
    case 4: case 6: case 9: case 11:
        daysInMonth = 30;
        break;
    case 2:
        daysInMonth = (year % 4 == 0) ? 29 : 28;
        break;
    default:
        throw new IllegalArgumentException("Invalid month: " + month);
}

System.out.println("Days in month: " + daysInMonth);

Output:

Days in month: 30

switch with String

Since Java 7, you can switch on String values directly — a huge quality-of-life improvement.

String command = "start";

switch (command) {
    case "start":
        System.out.println("Starting the engine...");
        break;
    case "stop":
        System.out.println("Stopping the engine...");
        break;
    case "pause":
        System.out.println("Pausing...");
        break;
    default:
        System.out.println("Unknown command: " + command);
}

Output:

Starting the engine...

Warning: String switching is case-sensitive"Start" will not match case "start". Also, passing null as the selector will throw a NullPointerException at runtime.

switch with enum

Switching on an enum type is clean and type-safe. The JVM can generate an efficient jump table for enum ordinals.

enum Season { SPRING, SUMMER, AUTUMN, WINTER }

Season current = Season.AUTUMN;

switch (current) {
    case SPRING:
        System.out.println("Time to plant!");
        break;
    case SUMMER:
        System.out.println("Beach season.");
        break;
    case AUTUMN:
        System.out.println("Leaves are falling.");
        break;
    case WINTER:
        System.out.println("Bundle up!");
        break;
}

Output:

Leaves are falling.

Note that inside a switch on an enum, you write the constant name alone (AUTUMN), not the fully qualified Season.AUTUMN.

Modern switch Expression (Java 14+)

Java 14 made switch expressions a standard feature (JEP 361). The new -> (arrow) syntax eliminates fall-through entirely and lets switch produce a value.

int day = 3;

String dayName = switch (day) {
    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";
    case 4 -> "Thursday";
    case 5 -> "Friday";
    case 6 -> "Saturday";
    case 7 -> "Sunday";
    default -> throw new IllegalArgumentException("Invalid day: " + day);
};

System.out.println(dayName);

Output:

Wednesday

Key differences from the classic form:

FeatureClassic switchSwitch Expression (14+)
Returns a valueNoYes
Fall-throughYes (without break)No — each arm is isolated
Syntaxcase X: + breakcase X ->
Multiple labels per armcase 1: case 2:case 1, 2 ->
Exhaustiveness checkNoYes (compiler enforces it)

Multi-label Arms

Group multiple values into a single arm with a comma:

int month = 6;

int daysInMonth = switch (month) {
    case 1, 3, 5, 7, 8, 10, 12 -> 31;
    case 4, 6, 9, 11            -> 30;
    case 2                      -> 28; // simplified; ignoring leap years
    default -> throw new IllegalArgumentException("Bad month: " + month);
};

System.out.println("Days: " + daysInMonth);

Output:

Days: 30

yield for Multi-Statement Arms

When an arm needs more than a single expression, use yield to return the value:

int score = 85;

String grade = switch (score / 10) {
    case 10, 9 -> "A";
    case 8     -> "B";
    case 7     -> "C";
    case 6     -> "D";
    default    -> {
        System.out.println("Score below 60 — needs improvement.");
        yield "F";
    }
};

System.out.println("Grade: " + grade);

Output:

Grade: B

Tip: yield is only valid inside switch expression blocks ({}). You cannot use return to produce a switch expression value; return would return from the entire method.

Pattern Matching in switch (Java 21)

Java 21 finalized pattern matching for switch (JEP 441), letting you match on type and deconstruct objects in a single step:

Object obj = 42;

String result = switch (obj) {
    case Integer i -> "Integer: " + i;
    case String s  -> "String: " + s;
    case null      -> "null value";
    default        -> "Something else: " + obj;
};

System.out.println(result);

Output:

Integer: 42

You can also add a guard condition with when:

Object value = -7;

String description = switch (value) {
    case Integer i when i > 0 -> "Positive integer";
    case Integer i when i < 0 -> "Negative integer";
    case Integer i            -> "Zero";
    default                   -> "Not an integer";
};

System.out.println(description);

Output:

Negative integer

This combines cleanly with sealed classes — the compiler can verify exhaustiveness when the sealed hierarchy is known at compile time.

Under the Hood

tableswitch vs. lookupswitch

The Java compiler emits one of two bytecode instructions depending on how dense your case values are:

  • tableswitch — used when case values are contiguous (or nearly so). The JVM builds an array-like jump table indexed by the selector value. This is an O(1) operation — extremely fast.
  • lookupswitch — used when case values are sparse. The JVM stores sorted key-offset pairs and performs a binary search. This is O(log n) — still very fast, but slightly more work than tableswitch.

You can inspect which instruction your code generates with the javap tool:

javap -c SwitchExample.class

String switch internals

Switching on String is implemented using hashCode() and equals(). The compiler first switches on hashCode() (using a tableswitch or lookupswitch), then confirms identity with equals() to handle hash collisions. This means String switch is O(1) for the hash lookup plus O(k) for string comparison, where k is the string length.

Switch expressions and exhaustiveness

With switch expressions the compiler enforces that all possible values are covered. For enum types, every constant must appear (or default must be present). For sealed class hierarchies, every permitted subtype must be handled. This compile-time guarantee prevents a whole class of runtime bugs.

Note: The JIT compiler can inline and optimize switch expressions aggressively because their control flow is simpler — no fall-through means cleaner data flow analysis.

Classic vs. Modern: Which Should You Use?

If you are on Java 14+, prefer switch expressions for most new code:

  • No accidental fall-through
  • Forces you to handle all cases
  • Can be used as part of an expression (assign, return, pass as argument)
  • Multi-label case 1, 2, 3 -> syntax is cleaner

Use the classic switch statement when you intentionally need fall-through behavior or when your codebase must support older Java versions.

For full details on the expression form, see Switch Expressions. For type-based matching, see Pattern Matching.

  • if-else Statement — the other main decision-making tool; better when conditions are complex boolean expressions rather than equality checks.
  • Switch Expressions — the modern Java 14+ form of switch that returns values, eliminates fall-through, and enforces exhaustiveness.
  • Pattern Matching — Java 16–21 enhancements that let switch match on types and deconstruct objects.
  • Enums — switch is at its most powerful and type-safe when combined with enum types.
  • Sealed Classes — pair with switch expressions for exhaustive, compiler-verified type hierarchies.
  • Control Statements — the full overview of every decision, looping, and jump statement in Java.
Last updated June 13, 2026
Was this helpful?