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.
Sir, I pressed 1. Why did it print every item?
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.
That sounds like a trap waiting to happen.
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.
What is switch?
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.
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
switch (…) that decides which case runs.case 2: or case "SAT" ->.switch at a glance
| Allowed selector types | byte, short, char, int, their wrappers, String (Java 7+), enum; any type with pattern matching (Java 21) |
|---|---|
| Not allowed (classic) | long, float, double, boolean |
| Case labels | Must be compile-time constants, no duplicates. Several labels: case "SAT", "SUN" -> |
| Statement vs expression | Statement: does something. Expression: int price = switch (day) { … }; produces a value. |
| Null selector | Throws 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).
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.
Arrow statement
case 1 -> System.out.println("Chai");
No fall-through, no breaks needed. Java 14+.
Switch expression
int price = switch (day) { case "SAT", "SUN" -> 15; default -> 20; };
Produces a value. Must be exhaustive.
Block with yield
case 3 -> { log(); yield 30; }
When a case needs several statements before producing its value.
switch or if-else?
| Situation | Better choice |
|---|---|
| One value compared with many fixed constants (menu, day, status) | switch |
Ranges: marks >= 60 | if-else |
| Conditions on several variables | if-else |
| Choosing a value for each enum constant | switch expression (the compiler checks you covered all) |
From buggy kiosk to modern switch
Write a classic switch with breaks
javac compiles this into a singleswitch (option) { case 1: item = "Chai"; break; case 2: item = "Samosa"; break; default: item = "Unknown"; }tableswitchinstruction: a jump table indexed by the value, so the JVM goes straight to case 2 without testing case 1 first.Understand what break does
Case labels are just jump targets inside one block of code.breakjumps to the end of the switch. Without it, the next case's statements run too.breakcompiles to agotopast the end; nothing stops execution at the next label.Group cases that share code
This is deliberate fall-through: both labels point to the same code. In arrow style, writecase "SAT": case "SUN": price = 15; break;case "SAT", "SUN" ->.Switch on a String
Java 7+ allows Strings. Comparison is case-sensitive and uses
javac first switches onequals.day.hashCode()with alookupswitch, then confirms withequals, because different strings can share a hash code. A null String throws NullPointerException.Turn it into a switch expression
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.int price = switch (day) { case "SAT", "SUN" -> 15; default -> 20; };
Jump tables and fall-through: both views
Left: the code and output. Right: the bytecode jump table the JVM uses.
- The selector
optionis 2. - Java goes directly to
case 2; it doesn't test case 1 first. breakends the switch. Result: Samosa.- Remove that break and execution falls through into case 3, overwriting item with Vada Pav.
- A switch on a String matches "SAT" exactly (case-sensitive).
- A switch expression with arrows produces a value and never falls through.
- javac compiled the cases into a tableswitch: a jump table indexed by the value.
- For option 2, the JVM reads entry 2 and jumps straight to @B.
breakis just agoto END.- Without the goto, nothing stops execution: it runs on into @C.
- For Strings, javac switches on the hash code with a
lookupswitch, then confirms withequals. - Arrow cases always jump to the end, and the compiler checks the switch expression is exhaustive.
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.
- Status: the order's status string is
"SHIPPED". - Jump: switch doesn't test every case one by one like an if-ladder; it goes straight to the matching label
case "SHIPPED":. - With break: it prints “On the way”, then
breakleaves the switch. One message, correct. - Forgot break? Execution falls through into the next cases and prints all the messages below. A real bug that sends customers wrong notifications.
- Modern fix (Java 14+):
case "SHIPPED" -> …runs only that case. No fall-through, no break to forget.
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.
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.
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).
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.
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.
Examples are simplified. Large systems often replace big switches with maps or polymorphism.
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);
}
}switch (option)The selector. Its value picks the case.case 2: … break;Classic case.breakstops fall-through.default:Runs when nothing matches. Here it also guaranteesitemis assigned.int price = switch (day) {A switch expression: its result goes intoprice.case "SAT", "SUN" -> 15;Two labels for one result. No break needed.};The semicolon ends the assignment statement.
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.
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.
day "SAT"
SAT: Family combo at Rs 799
Approach
switch (day)on a String.case "SAT", "SUN" ->groups two labels.defaulthandles 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.
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.
key 3
Card blocked. A new card will be sent. Thank you for banking with us
Approach
switch (key)on an int.- Each case ends with
breakso execution doesn't fall into the next case. defaultfor 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.
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.
month 2, year 2028; month 9
Feb 2028: 29 days, wages Rs 18850 Sep: 30 days
Approach
- Arrow cases with several labels:
case 4, 6, 9, 11 -> 30. - A block with
yieldfor February's leap-year rule. - 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.
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 untilbreak;defaulthandles the rest. - Arrow
case X ->never falls through; several labels:case 1, 2 ->. - Switch expression: produces a value, ends with
};, must be exhaustive, usesyieldin 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.
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.
day = 5
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);
}
}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.
Simple calculator
Given two numbers and an operator character (+, −, *, /), print the result. Print “Unknown operator” for anything else, and handle division by zero.
a = 12, b = 4, op = '/'
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");
}
}
}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.
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.
month = 8
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);
}
}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.
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.
day = "WED"
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");
}
}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.
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.
status = "OUT_FOR_DELIVERY"
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);
}
}How it works:
- 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.
- javac checks that
messageandnextActionare assigned on every path; the throwing default counts as a path that never continues. - 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.
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.)
(CARD, 2500), (WALLET, 3200), (UPI, 999)
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));
}
}How it works:
return switch (…)returns the switch expression's value straight from the method.- 1.8% is calculated as
amount * 18 / 1000using integers, avoiding the double rounding problems from Lesson 2.2. Multiplying first keeps precision: 2500 × 18 = 45000, ÷ 1000 = 45. - The wallet cap uses
Math.min: 1% of 3200 is 32, but the cap is 20. - 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.
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.
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.
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 nullon Java 21. - Normalise input before switching:
command.trim().toLowerCase(), so “Balance ” and “balance” match. - Always include a
defaultthat 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.
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 aMap<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.