switch statement and switch expressions

When one value decides between many fixed options (a menu choice, a day of the week, an order status), a long else-if ladder gets clumsy. switch handles it cleanly. You'll learn the classic form with its famous fall-through trap, the modern arrow-style switch expression, and how the JVM jumps straight to the right case.

Module 3 · Control Flow⏱ 17 minBeginnerNo install needed
1 The story

The kiosk that served everything

Riya upgrades the canteen kiosk: press 1 for Chai, 2 for Samosa, 3 for Vada Pav. She uses a switch. A friend presses 1 and the screen shows: Chai, Samosa, Vada Pav, Unknown item. The canteen uncle is not amused.

option = 1 case 1: Chai case 2: Samosa case 3: Vada Pav default no break →falls through! Kiosk screen ChaiSamosaVada PavUnknown item myphatshala.in
Riya

Sir, I pressed 1. Why did it print every item?

Arjun Sir

A classic switch jumps to the matching case, then keeps running downwards until it meets a break or the end. That's called fall-through. You forgot the breaks.

Riya

That sounds like a trap waiting to happen.

Arjun Sir

It is. That's why modern Java added the arrow form, case 1 ->, which never falls through. Let's see both, and how the JVM finds the right case without checking each one.

2 Definition

What is switch?

Formal definition (write this in exams)

A switch transfers control to one of several case labels based on the value of a selector expression of type byte, short, char, int (or their wrappers), String or an enum (and, since Java 21, other reference types with patterns). In a switch statement with colon labels, execution continues into following cases until a break (fall-through). A switch expression (Java 14+) uses case … -> labels, never falls through, produces a value, and must be exhaustive.

In simple words

switch looks at one value and jumps to the matching case, like a lift going straight to your floor. The old style keeps going down unless you say break. The new arrow style runs only the matching case and can hand back a value.

Words you'll meet in this lesson

SelectorThe value in switch (…) that decides which case runs.
case labelA constant to compare with: case 2: or case "SAT" ->.
defaultRuns when no case matches, like else.
Fall-throughContinuing into the next case's code because there's no break.
yieldReturns a value from a block inside a switch expression.
ExhaustiveEvery possible value is covered, by cases or a default.

switch at a glance

Allowed selector typesbyte, short, char, int, their wrappers, String (Java 7+), enum; any type with pattern matching (Java 21)
Not allowed (classic)long, float, double, boolean
Case labelsMust be compile-time constants, no duplicates. Several labels: case "SAT", "SUN" ->
Statement vs expressionStatement: does something. Expression: int price = switch (day) { … }; produces a value.
Null selectorThrows NullPointerException (unless there's a case null, Java 21)

Exam tip: in a classic switch, forgetting break causes fall-through, and default can be written anywhere (not only last). Arrow cases (->) never fall through. A switch expression over an int or String needs a default; over an enum it must cover every constant (or have a default).

3 Types and ways to use it

Four ways to write a switch

Classic statement (colon)

case 1: … break;
Works on every Java version. Needs a break in each case. Fall-through can be deliberate, but usually it's a bug.

Old and common

Arrow statement

case 1 -> System.out.println("Chai");
No fall-through, no breaks needed. Java 14+.

Safer

Switch expression

int price = switch (day) { case "SAT", "SUN" -> 15; default -> 20; };
Produces a value. Must be exhaustive.

Modern favourite

Block with yield

case 3 -> { log(); yield 30; }
When a case needs several statements before producing its value.

Multi-line cases

switch or if-else?

SituationBetter choice
One value compared with many fixed constants (menu, day, status)switch
Ranges: marks >= 60if-else
Conditions on several variablesif-else
Choosing a value for each enum constantswitch expression (the compiler checks you covered all)
4 Step by step

From buggy kiosk to modern switch

  1. Write a classic switch with breaks

    switch (option) {
        case 1:
            item = "Chai";
            break;
        case 2:
            item = "Samosa";
            break;
        default:
            item = "Unknown";
    }
    javac compiles this into a single tableswitch instruction: a jump table indexed by the value, so the JVM goes straight to case 2 without testing case 1 first.
  2. Understand what break does

    break jumps to the end of the switch. Without it, the next case's statements run too.

    Case labels are just jump targets inside one block of code. break compiles to a goto past the end; nothing stops execution at the next label.
  3. Group cases that share code

    case "SAT":
    case "SUN":
        price = 15;
        break;
    This is deliberate fall-through: both labels point to the same code. In arrow style, write case "SAT", "SUN" ->.
  4. Switch on a String

    Java 7+ allows Strings. Comparison is case-sensitive and uses equals.

    javac first switches on day.hashCode() with a lookupswitch, then confirms with equals, because different strings can share a hash code. A null String throws NullPointerException.
  5. Turn it into a switch expression

    int price = switch (day) {
        case "SAT", "SUN" -> 15;
        default -> 20;
    };
    Note the semicolon after the closing brace: the whole switch is an expression inside an assignment. javac refuses to compile it if some input has no matching case.
5 See it happen

Jump tables and fall-through: both views

Left: the code and output. Right: the bytecode jump table the JVM uses.

How switch finds its case 6 steps
What you see switch (option) { // option = 2 case 1: item = "Chai"; break; case 2: item = "Samosa"; break; // break removed! case 3: item = "Vada Pav"; break; default: item = "Unknown"; } switch (day) { // day = "SAT" case "SAT": … case "SUN": …} int price = switch (day) { case "SAT", "SUN" -> 15; default -> 20;}; (running…) item = Samosa item = Vada Pav ← overwritten! matched "SAT" price = 15 option is 2. Which case runs? Straight to case 2. Case 1 is never tested. break jumps out of the switch. No break: execution falls into case 3. Strings work too, case-sensitively. Arrow form: a value, no fall-through. myphatshala.in
Behind the screen tableswitch 1 to 3 1 → @A2 → @B3 → @Cdefault → @D @A: "Chai" · goto END @B: "Samosa"· goto END @C: "Vada Pav" · goto END @D: "Unknown" END: … The JVM uses option − 1 as an index into the table. One lookup, one jump, however many cases there are. break is compiled to goto END. No goto after @B, so the CPU simply continueswith the next instructions: @C's code. 1. "SAT".hashCode() = 81862 lookupswitch81862 → check "SAT" 82476 → check "SUN" 2. confirm with day.equals("SAT") → true ✓ Hashes can collide, so equals() makes sure. Each arrow case ends with an automatic jump to the end: no fall-through possible. javac checks every possible input has a case; otherwise: does not cover all possible input values myphatshala.in
6 Real-world use

switch in real systems

switch is the natural fit whenever a status, type or code picks one of several fixed actions.

Watch it with an example: order status messages

A shopping app sends one message per order status. See switch jump to the matching case, what happens if you forget break, and how the arrow form avoids it.

switch (status) on "SHIPPED" 5 steps
Animated example status = "SHIPPED" case "PLACED": print("Order placed") case "SHIPPED": print("On the way") case "OUT": print("Out for delivery") default: print("Check status") break; Console On the way Out for delivery Check status fall-through: 3 messages instead of 1! case "SHIPPED" -> arrow form: no break needed myphatshala.in
PLACED → confirm SMSSHIPPED → tracking SMSDELIVERED → rating SMS

1. Order status notifications

E-commerce systems switch on an order status enum to decide which message to send. A switch expression over the enum makes the compiler remind you when a new status is added.

status = SHIPPED→switch (status)→Tracking SMS
IVR menu

2. Menus: IVR, ATM and kiosk screens

“Press 1 for balance, 2 for mini statement…” is a switch on the key pressed, with a default for invalid keys.

Key 2→case 2 -> miniStatement()→Spoken reply
UPI / CARD /NETBANKING

3. Choosing a processor by type

A payment service picks the right processor by payment mode, and a file importer picks a parser by file type. In bigger systems this switch is often replaced by a map of handlers or polymorphism (Module 7).

mode = UPI→switch → UpiProcessor→Payment
$ tool --mode=importexport | report

4. Command-line options

Tools read a command name from args and switch on it: import, export, report, with a default that prints usage help.

args[0] = "export"→switch (args[0])→Run export
HTTP 404

5. Mapping codes to messages

HTTP status codes, error codes from a bank, or day numbers to names: a switch expression turns a code into a message or category in one readable block.

code = 404→switch expression→“Not found”

Examples are simplified. Large systems often replace big switches with maps or polymorphism.

7 Run the code

The kiosk, both styles

public class Main {
    public static void main(String[] args) {
        int option = 2;
        String item;
        switch (option) {                 // classic switch statement
            case 1:
                item = "Chai";
                break;
            case 2:
                item = "Samosa";
                break;
            case 3:
                item = "Vada Pav";
                break;
            default:
                item = "Unknown";
        }
        String day = "SAT";
        int price = switch (day) {        // modern switch expression
            case "SAT", "SUN" -> 15;
            default -> 20;
        };
        System.out.println(item + " today costs Rs " + price);
    }
}
Samosa today costs Rs 15
  • switch (option)The selector. Its value picks the case.
  • case 2: … break;Classic case. break stops fall-through.
  • default:Runs when nothing matches. Here it also guarantees item is assigned.
  • int price = switch (day) {A switch expression: its result goes into price.
  • case "SAT", "SUN" -> 15;Two labels for one result. No break needed.
  • };The semicolon ends the assignment statement.
8 Line by line

One jump in, one jump out

Watch how the tracer goes straight from the switch to case 2, skipping case 1 entirely.

Worked examples: real problems, solved and traced in memory

A switch picks one path from many fixed values: days, menu choices, status codes. These programs use both the classic form and the modern arrow form (Java 14+). In the trace, watch the JVM jump straight to the matching case.

Example 1Restaurant · offers

Today's offer by weekday

Problem statement

A restaurant runs a different offer each day. Given the day name, print the offer. Weekends share one offer. Use the arrow form, which never falls through.

Input
day "SAT"
Expected output
SAT: Family combo at Rs 799

Approach

  1. switch (day) on a String.
  2. case "SAT", "SUN" -> groups two labels.
  3. default handles anything unexpected.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Banking · IVR

Phone-banking menu

Problem statement

A bank's phone menu: press 1 for balance, 2 for mini statement, 3 to block a card, 0 for an agent. Handle the key pressed with a classic switch and break.

Input
key 3
Expected output
Card blocked. A new card will be sent.
Thank you for banking with us

Approach

  1. switch (key) on an int.
  2. Each case ends with break so execution doesn't fall into the next case.
  3. default for invalid keys.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3Payroll · calendar

Days in a month with grouped cases

Problem statement

A payroll system pays daily wages, so it needs the number of days in each month. Group months with the same length. February depends on whether it's a leap year.

Input
month 2, year 2028; month 9
Expected output
Feb 2028: 29 days, wages Rs 18850
Sep: 30 days

Approach

  1. Arrow cases with several labels: case 4, 6, 9, 11 -> 30.
  2. A block with yield for February's leap-year rule.
  3. Compute wages for February 2028 at Rs 650/day.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 3.2 in one screen

  • Selectors: byte, short, char, int (+ wrappers), String, enum. Not long/float/double/boolean (classic).
  • Classic case X: falls through until break; default handles the rest.
  • Arrow case X -> never falls through; several labels: case 1, 2 ->.
  • Switch expression: produces a value, ends with };, must be exhaustive, uses yield in blocks.
  • Bytecode: tableswitch (dense), lookupswitch (sparse); Strings use hashCode + equals.
  • null selector → NullPointerException. String cases are case-sensitive.

Interview and viva questions

What is fall-through, and is it ever useful?

Fall-through is execution continuing into the next case when there's no break. It's useful when several labels share code (case "SAT": case "SUN":), but accidental fall-through is a common bug. Arrow syntax avoids it.

What is the difference between a switch statement and a switch expression?

A switch statement performs actions and may be non-exhaustive. A switch expression evaluates to a value, can be used on the right side of an assignment or as an argument, must be exhaustive, and uses yield for block cases.

Why can't we switch on long or double?

The JVM's switch instructions work on int keys, and floating-point equality is unreliable. byte, short and char are promoted to int; Strings and enums are handled by mapping them to ints first.

How does a switch on an enum work?

You write the constant names without the enum prefix (case UPI). javac maps each constant to an int using its ordinal, often through a small lookup array, and uses tableswitch.

What's new in switch in Java 21?

Pattern matching for switch: cases can test types and extract values (case Integer i ->), add guards with when, and handle case null. It's covered in the Modern Java module.

Practice problems

Try each one in the live editor before opening the solution. The last two are real business rules.

P1Easy

Day number to day name

Given a day number from 1 (Monday) to 7 (Sunday), print the day's name. Print “Invalid day” for anything else.

Input
day = 5
Expected output
Friday
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        int day = 5;
        String name = switch (day) {
            case 1 -> "Monday";
            case 2 -> "Tuesday";
            case 3 -> "Wednesday";
            case 4 -> "Thursday";
            case 5 -> "Friday";
            case 6 -> "Saturday";
            case 7 -> "Sunday";
            default -> "Invalid day";
        };
        System.out.println(name);
    }
}
Friday

How it works: one value compared with seven constants is exactly what switch is for. A switch expression gives the name directly, and the default makes it exhaustive. With dense values 1–7, the JVM uses a fast tableswitch.

P2Easy

Simple calculator

Given two numbers and an operator character (+, −, *, /), print the result. Print “Unknown operator” for anything else, and handle division by zero.

Input
a = 12, b = 4, op = '/'
Expected output
12 / 4 = 3
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        int a = 12, b = 4;
        char op = '/';
        switch (op) {
            case '+' -> System.out.println(a + " + " + b + " = " + (a + b));
            case '-' -> System.out.println(a + " - " + b + " = " + (a - b));
            case '*' -> System.out.println(a + " * " + b + " = " + (a * b));
            case '/' -> {
                if (b == 0) {
                    System.out.println("Cannot divide by zero");
                } else {
                    System.out.println(a + " / " + b + " = " + (a / b));
                }
            }
            default -> System.out.println("Unknown operator");
        }
    }
}
12 / 4 = 3

How it works: switch works on char. The division case needs two statements (a check, then the print), so it uses a { } block. Notice the brackets around (a + b): without them, + would join text instead of adding.

P3Medium

Month to financial quarter

In India the financial year starts in April. Given a month number (1–12), print its financial quarter: Apr–Jun is Q1, Jul–Sep Q2, Oct–Dec Q3 and Jan–Mar Q4.

Input
month = 8
Expected output
Month 8 is in Q2
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        int month = 8;
        String quarter = switch (month) {
            case 4, 5, 6 -> "Q1";
            case 7, 8, 9 -> "Q2";
            case 10, 11, 12 -> "Q3";
            case 1, 2, 3 -> "Q4";
            default -> "Invalid month";
        };
        System.out.println("Month " + month + " is in " + quarter);
    }
}
Month 8 is in Q2

How it works: several labels share one result: case 7, 8, 9 ->. With a classic switch you'd write three case lines falling through to one body. The order of cases doesn't matter here, because each value appears only once.

P4Medium

Count days until the weekend

Given a day name in capitals, print how many days are left until Saturday. Print 0 for Saturday and Sunday. Use yield for the weekend case and include a message.

Input
day = "WED"
Expected output
WED: 3 day(s) to the weekend
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String day = "WED";
        int left = switch (day) {
            case "MON" -> 5;
            case "TUE" -> 4;
            case "WED" -> 3;
            case "THU" -> 2;
            case "FRI" -> 1;
            case "SAT", "SUN" -> {
                System.out.println("It's the weekend!");
                yield 0;
            }
            default -> throw new IllegalArgumentException("Unknown day: " + day);
        };
        System.out.println(day + ": " + left + " day(s) to the weekend");
    }
}
WED: 3 day(s) to the weekend

How it works: a String switch uses hashCode() then equals(). The weekend case needs two steps, so it's a block that ends with yield 0. For invalid input, default throws an exception instead of returning a fake number: silently guessing would hide bugs.

P5Business problem

Order status message for customers

An e-commerce app shows a message and the next action for each order status: PLACED, PACKED, SHIPPED, OUT_FOR_DELIVERY, DELIVERED, CANCELLED. Any other status must be reported as an error, not shown to the customer.

Input
status = "OUT_FOR_DELIVERY"
Expected output
Your order is out for delivery today.
Next action: SHOW_OTP
Show solution and explanation
public class Main {
    public static void main(String[] args) {
        String status = "OUT_FOR_DELIVERY";
        String message;
        String nextAction;
        switch (status) {
            case "PLACED" -> { message = "We've received your order."; nextAction = "ALLOW_CANCEL"; }
            case "PACKED" -> { message = "Your order is packed."; nextAction = "ALLOW_CANCEL"; }
            case "SHIPPED" -> { message = "Your order has been shipped."; nextAction = "SHOW_TRACKING"; }
            case "OUT_FOR_DELIVERY" -> { message = "Your order is out for delivery today."; nextAction = "SHOW_OTP"; }
            case "DELIVERED" -> { message = "Delivered. Enjoy!"; nextAction = "ASK_RATING"; }
            case "CANCELLED" -> { message = "Your order was cancelled."; nextAction = "SHOW_REFUND"; }
            default -> throw new IllegalStateException("Unexpected order status: " + status);
        }
        System.out.println(message);
        System.out.println("Next action: " + nextAction);
    }
}
Your order is out for delivery today. Next action: SHOW_OTP

How it works:

  1. Each status sets two values, so each case is a small block. Arrow cases can't fall through, so the SHIPPED SMS bug from the scenarios below can't happen.
  2. javac checks that message and nextAction are assigned on every path; the throwing default counts as a path that never continues.
  3. An unknown status is a data problem, so the code fails loudly instead of showing customers a wrong message. In real code, the status would be an enum (Lesson 10.4), and a switch expression without default would make the compiler catch new statuses.
P6Business problem

Payment gateway fee calculator

A merchant pays a fee per payment: UPI is free; CARD costs 1.8%; NETBANKING costs a flat ₹10; WALLET costs 1% but at most ₹20. Amounts are whole rupees and fees are rounded down to whole rupees. Print the fee for each payment. (Rates are illustrative.)

Input
(CARD, 2500), (WALLET, 3200), (UPI, 999)
Expected output
CARD on Rs 2500: fee Rs 45
WALLET on Rs 3200: fee Rs 20
UPI on Rs 999: fee Rs 0
Show solution and explanation
public class Main {
    static int fee(String mode, int amount) {
        return switch (mode) {
            case "UPI" -> 0;
            case "CARD" -> amount * 18 / 1000;          // 1.8% in whole numbers
            case "NETBANKING" -> 10;
            case "WALLET" -> Math.min(amount / 100, 20); // 1%, capped at Rs 20
            default -> throw new IllegalArgumentException("Unsupported mode: " + mode);
        };
    }

    public static void main(String[] args) {
        System.out.println("CARD on Rs 2500: fee Rs " + fee("CARD", 2500));
        System.out.println("WALLET on Rs 3200: fee Rs " + fee("WALLET", 3200));
        System.out.println("UPI on Rs 999: fee Rs " + fee("UPI", 999));
    }
}
CARD on Rs 2500: fee Rs 45 WALLET on Rs 3200: fee Rs 20 UPI on Rs 999: fee Rs 0

How it works:

  1. return switch (…) returns the switch expression's value straight from the method.
  2. 1.8% is calculated as amount * 18 / 1000 using integers, avoiding the double rounding problems from Lesson 2.2. Multiplying first keeps precision: 2500 × 18 = 45000, ÷ 1000 = 45.
  3. The wallet cap uses Math.min: 1% of 3200 is 32, but the cap is 20.
  4. An unsupported mode throws, so a typo like "Card" can't silently charge ₹0.

Real-world scenario questions (asked in company interviews)

switch bugs are usually missing cases or missing breaks. Decide your answer first, then open the model answer.

E-commerce · notificationsFresher

Customers get two SMS for one event

When an order is shipped, customers receive both a “shipped” and a “delivered” SMS. The notification code is a classic switch (status) with case SHIPPED: sendShippedSms(); followed by case DELIVERED: sendDeliveredSms(); break;. What's wrong, and how do you prevent it in future?

The interviewer is checking: do you recognise accidental fall-through and know modern alternatives?

Show a strong answer

The SHIPPED case has no break, so execution falls through into the DELIVERED case and sends the second SMS.

  • Immediate fix: add the missing break.
  • Better: rewrite with arrow cases (case SHIPPED -> sendShippedSms();), which can't fall through.
  • Turn on the compiler/IDE warning for fall-through (-Xlint:fallthrough) and add a test per status.
  • Check whether wrong messages were sent and inform customers if needed.

Avoid: just adding the break and moving on without a test.

Payments · new feature1–3 years

A new payment mode silently does nothing

The team added WALLET to the PaymentMode enum. Wallet payments now succeed in the UI, but no fee is charged and no receipt is sent. Several classic switch statements across the code don't have a WALLET case. How could the compiler have caught this?

The interviewer is checking: do you know about exhaustive switch expressions over enums?

Show a strong answer

A classic switch statement doesn't have to cover every enum constant, so the new value just matched nothing (or a vague default) without any error.

  • Use switch expressions over the enum without a default: int fee = switch (mode) { case UPI -> 0; case CARD -> 2; case NETBANKING -> 1; };. Adding WALLET then causes a compile error everywhere it isn't handled.
  • Where a default is truly needed, make it throw an exception instead of silently doing nothing.
  • For behaviour that varies by type, consider putting it on the enum itself or in separate classes (polymorphism).

Avoid: adding default -> {} everywhere “to be safe”. It hides exactly this kind of bug.

Chatbot · command handlingFresher

The bot crashes when a message is empty

A support chatbot reads a command and does switch (command) { case "balance": … case "help": … }. It crashes with a NullPointerException for some users, and replies “unknown command” when users type “Balance” with a capital B. How would you make it robust?

The interviewer is checking: do you know a String switch is case-sensitive and throws on null?

Show a strong answer
  • A switch on a null String throws NullPointerException. Check for null (or empty) first, or use case null on Java 21.
  • Normalise input before switching: command.trim().toLowerCase(), so “Balance ” and “balance” match.
  • Always include a default that shows help text instead of failing.
  • Consider mapping commands to an enum once, at the input boundary.

Avoid: adding case "Balance", case "BALANCE", case "balance "… for every variation.

Logistics · growing codebase1–3 years

A 40-case switch that keeps growing

A shipping service calculates charges with a switch on courier partner name that now has 40 cases, each with several lines of logic. Every new partner means editing this one huge method, and merge conflicts are constant. What would you suggest?

The interviewer is checking: do you know when switch stops being the right design?

Show a strong answer
  • Move each partner's logic into its own class implementing a common interface, e.g. ShippingCalculator, and look them up from a Map<String, ShippingCalculator> (or let a framework register them).
  • Adding a partner then means adding a new class, not editing a shared method (the open/closed principle).
  • If the differences are only numbers (rates, slabs), put them in configuration or a database table instead of code.
  • Refactor step by step with tests, moving a few cases at a time.

Avoid saying: “switch is always bad.” It's great for small, stable sets of cases.