Classic switch and fall-through
On This Lesson 11 topics
Lesson Overview
An else if chain is perfect for ranges such as "80 and above". But many decisions are not about ranges. They pick one choice out of a fixed list: day 1 to 7, a menu option, a command word, a month number. You could write such a list as a long chain of if (day == 1) ... else if (day == 2) ..., but Java offers a tool built for it, the switch statement.
The classic form of switch has one famous trap, fall-through: if you forget a small keyword, the program runs more code than you meant. Understanding fall-through is useful twice: it explains a very common bug, and it explains why the modern form in the next lesson was invented. Think of a junction where the sign sends you to a roundabout exit, but without a barrier you keep driving into the next exit too.
This lesson builds on Lessons 1 and 2 (conditions and chains) and on Module 9 (int, char, and String). Lesson 4 shows the arrow form, which removes the trap.
Learning Objectives
By the end of this lesson you will be able to:
- Write a classic
switchstatement withcaselabels,break, anddefault. - Predict the output of a switch in which a
breakis missing (fall-through). - Group several labels onto one action on purpose.
- Name the types a
switchcan use and explain why ranges do not fit. - Decide whether a given decision is better written as a
switchor anifchain. - Read the compiler messages for duplicate labels and for a selector type that is not allowed.
Written Explanation
The shape of a switch
switch (day) {
case 6:
case 7:
System.out.println("Weekend");
break;
default:
System.out.println("Weekday");
}Read it like this. Java evaluates the expression in the parentheses once, the selector. It looks for the first case label whose constant equals the selector, and starts running statements from that label. It keeps running until it reaches a break or the closing brace. If no label matches, it jumps to default, if there is one; if there is none, the whole switch does nothing.
Each case must be a constant (a literal such as 6 or "stop", or a final constant), not a variable and not a test. Two labels with the same value are a compile error.
What a switch can look at
The Java Language Specification lists the selector types allowed in the classic form that this lesson uses: char, byte, short, int, their wrapper classes, String, and enum types (which you will meet later). A long, float, double, or boolean selector is rejected.
A switch only compares for equality. There is no way to write "greater than 80" in a case label, so ranges belong to if chains. When the selector is a String, the text is compared by content, so "stop" matches "stop". (Why that is not the same as == on strings is explained in a later module.) Be careful with letter case: "Stop" does not match the label "stop".
break and fall-through
The break statement ends the switch and continues after the closing brace. It is the barrier at the exit. If you omit it, Java does not stop at the next case label: labels are only entry points, not walls, so execution falls through into the following statements, whatever label they carry. Fall-through is how the language is defined for the classic case X: form; it is not a bug in Java.
Sometimes you want it, as in the weekend example: label 6 has no statements and no break, so it falls into label 7's block. Several labels share one action. That is intentional fall-through, and it is the only kind worth writing. Everywhere else, end each group with break.
default
default catches every value that matches no label. It can appear anywhere in the switch, but by convention it is last, and then it does not need a break. Include it almost always: it is the classic else, the place where you handle values you did not expect (a month number 13, an unknown command).
Switch or if?
| Situation | Better tool |
|---|---|
| One value compared with several fixed constants | switch |
| Ranges ("at least 80") or comparisons | if / else if |
| Two or more variables in the test | if with && or or logic |
| Only two outcomes | if / else |
Examples
Example 1: weekend or weekday (simple)
int day = 6;
switch (day) {
case 6:
case 7:
System.out.println("Weekend");
break;
default:
System.out.println("Weekday");
}Output:
WeekendLabel 6 matches, there is nothing to run there, so execution slides into label 7's println, then break leaves the switch. Any other value goes to default. (Whether days 6 and 7 are the weekend depends on the country and on how you number the days; here it is simply a rule we chose.)
Example 2: days in a month and a command word (practical)
int m = 4;
int days;
switch (m) {
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = 28;
break;
default:
days = 31;
}
System.out.println("Month " + m + " has " + days + " days");Output:
Month 4 has 30 daysFour labels share one action, and February is handled alone (ignoring leap years for now). Because every path, including default, assigns days, the compiler accepts the later use of days.
A String selector works the same way:
String cmd = "stop";
switch (cmd) {
case "go":
System.out.println("Starting");
break;
case "stop":
System.out.println("Stopping");
break;
default:
System.out.println("Unknown command");
}Output: Stopping.
Example 3: the missing break (realistic)
A menu program prints what each option does. The author forgets two break statements:
int option = 2;
switch (option) {
case 1:
System.out.println("Open");
case 2:
System.out.println("Save");
case 3:
System.out.println("Exit");
break;
default:
System.out.println("Unknown");
}Real output:
Save
ExitThe user chose option 2, "Save", and the program also exited. Execution entered at label 2, found no break, and fell into label 3. With a break after Save, only Save would print.
Fall-through is sometimes used deliberately for cumulative rules:
int level = 3;
switch (level) {
case 3:
System.out.println("admin rights");
case 2:
System.out.println("edit rights");
case 1:
System.out.println("read rights");
}Output: admin rights, edit rights, read rights on three lines. It works, but a reader cannot tell it from a forgotten break, so if you ever write it, add a comment saying it is intentional.
Backend Insight
Backends are full of "dispatch on a code" decisions: an HTTP method (GET, POST, DELETE), an order status (NEW, PAID, SHIPPED), a message type, a command from a queue. These are exact-match choices over a small fixed set, which is the sweet spot of switch.
The missing-break bug is dangerous there for a specific reason: it does not crash. A PAID order that falls into the SHIPPED code may trigger a shipment for something not yet ready, and the program carries on as if everything were fine. Many teams therefore prefer the arrow form (next lesson) and enable compiler or IDE warnings for fall-through. If you must use the classic form, review every case for its break the way you review a boundary: it is the line that is easy to miss and costly when missing.
Also keep a default that does something loud when it sees a value nobody planned for, such as printing an error. A silent default hides bad data.
Common Mistakes
Mistake 1: Forgetting break
- Incorrect: a case block with no
breakand no comment. - Correct: end every group with
break, or mark intentional fall-through.
See Example 3. If more lines print than you expect, check the cases above the one you chose.
Mistake 2: Trying to use a range or condition in a case
- Incorrect:
case score >= 80: - Correct: use an
else ifchain, or compute a small whole number first (such asscore / 10).
The compiler's actual message for the incorrect line:
L03c.java:14: error: incompatible types: boolean cannot be converted to int
case score >= 80: System.out.println("B"); break;
^Mistake 3: Duplicate labels or a selector type that is not allowed
Two labels with the same value:
L03c.java:6: error: duplicate case label
case 1: System.out.println("b"); break;
^A double selector:
L03c.java:9: error: selector type double is not allowed
switch (d) {
^(The messages were produced by JDK 21 and may differ slightly in other versions.)
Mistake 4: Leaving out default
- Incorrect: a switch over a menu option with no
default; an invalid option silently does nothing. - Correct: add a
defaultthat reports the bad value.
Mistake 5: Wrong letter case with strings
"STOP" does not match case "stop":. Normalize the text first or list every spelling you accept.
Exercises
Level A: Foundation
A1. Predict the output for n = 2:
switch (n) {
case 1: System.out.println("one"); break;
case 2: System.out.println("two");
case 3: System.out.println("three"); break;
default: System.out.println("other");
}What is printed for n = 1, n = 3, and n = 9?
Show solution A1
A1. For n = 2: two and then three, because there is no break after two. n = 1: one. n = 3: three. n = 9: other.
A2. Which of these selector types are allowed in a classic switch: int, String, double, char, boolean?
Show solution A2
A2. int, String, and char are allowed. double and boolean are not.
A3. In one sentence: what is fall-through?
Show solution A3
A3. Fall-through means that when a case has no break, execution continues into the statements of the next label instead of leaving the switch.
Level B: Application
B1. Write a switch that turns an int day number (1 to 7, Monday is 1) into a name, with Unknown day for anything else. Use break correctly.
Show solution B1
B1.
String name;
switch (day) {
case 1: name = "Monday"; break;
case 2: name = "Tuesday"; break;
case 3: name = "Wednesday"; break;
case 4: name = "Thursday"; break;
case 5: name = "Friday"; break;
case 6: name = "Saturday"; break;
case 7: name = "Sunday"; break;
default: name = "Unknown day";
}B2. A parking machine charges by vehicle code: 'C' car costs 5, 'M' motorbike costs 2, 'T' and 'B' (truck and bus) cost 12, anything else prints Unknown vehicle. Write it with a char selector and grouped labels.
Show solution B2
B2.
int fee;
switch (code) {
case 'C':
fee = 5;
break;
case 'M':
fee = 2;
break;
case 'T':
case 'B':
fee = 12;
break;
default:
System.out.println("Unknown vehicle");
fee = 0;
}B3. The code below should print Closed for Sunday (7) only, but it prints more. Predict the output for day = 7 and fix it.
switch (day) {
case 7:
System.out.println("Closed");
default:
System.out.println("Open");
}Show solution B3
B3. For day = 7 it prints Closed and then Open, because label 7 falls into default. Fix: add break; after the Closed line.
Level C: Challenge
C1. Rewrite this switch as an if chain, then explain which version you would keep and why.
switch (code) {
case 200: msg = "OK"; break;
case 404: msg = "Not found"; break;
case 500: msg = "Server error"; break;
default: msg = "Other";
}Show solution C1
C1.
if (code == 200) { msg = "OK"; }
else if (code == 404) { msg = "Not found"; }
else if (code == 500) { msg = "Server error"; }
else { msg = "Other"; }Either is correct. The switch states "one value against several constants" and avoids repeating code ==. The if chain cannot fall through by accident. Both answers are acceptable if justified; the arrow form in the next lesson combines both benefits.
C2. A shop gives a discount percent by a customer score from 0 to 100: 0 to 49 gives 0, 50 to 79 gives 5, 80 to 100 gives 10. A classmate says "use a switch on score / 10". Explain how that could work, list the labels you would need, and say whether you prefer it to an if chain.
Show solution C2
C2. score / 10 is integer division, so 0 to 49 give 0 to 4, 50 to 79 give 5 to 7, and 80 to 99 give 8 and 9, and 100 gives 10. Labels: case 0: case 1: case 2: case 3: case 4: for 0, case 5: case 6: case 7: for 5, case 8: case 9: case 10: for 10. It works only because the boundaries sit at multiples of 10, it breaks if a boundary moves to 75, and it accepts -5 / 10, which is 0 in integer division, so negative scores need a separate check. An if chain with >= 80 and >= 50 is clearer and safer here.
Mini Challenge
Write a small "traffic light" program with String light set to "red", "yellow", or "green". Use a classic switch to print Stop, Get ready, or Go, and a default that prints Light is broken. Then deliberately remove one break, predict what changes before running, and run it. Finally restore the break.
Check your work: you predicted the extra output correctly, and you can point at the label where execution entered and the label it fell into.
Summary & Next Lesson
Key takeaways
switchcompares one selector value for equality against constantcaselabels; it suits fixed choices, not ranges.- Execution starts at the matching label and continues until
breakor the end of the switch. Withoutbreakit falls through. - Grouped labels (
case 6: case 7:) share an action on purpose; any other fall-through is probably a bug. - Classic
switchallowschar,byte,short,int, wrappers,String, and enums, notlong,double, orboolean. - Include a
defaultfor unexpected values.
Next lesson
The classic switch works but is easy to get wrong. The next lesson, "Switch expressions (modern Java)", introduces the arrow form that never falls through and can produce a value directly.
Self-check
- What starts running when no label matches and there is a
default? - What happens to the code after a matched label if there is no
break? - Can a
caselabel containscore >= 80? - Why is
doublerejected as a selector type? - Name one decision better suited to
switchand one better suited toelse if.
Show answers
- The
defaultblock. - It keeps running into the following labels' statements until a
breakor the end of the switch. - No: a label must be a constant, and ranges need an
ifchain. - The classic switch is defined only for the listed integer-like types, wrappers, strings, and enums; the specification does not allow
double. - Switch: day number to name. Else-if: score to grade (ranges).
References
- Oracle, The Java Language Specification, Java SE 21, Chapter 14, section on the
switchstatement. Supports the rules that old-style labels fall through, that arrow rules do not, which selector types are allowed, and that duplicate constants are an error. - The outputs and compiler messages were produced with JDK 21; wording may vary in other versions.
Practice
Apply this lesson with the module's lab, exercises and quiz.
Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.