By WebNest Studio Engineering Team
Java - Core Tutorial
Java Switch Statement
The switch statement is a multi-way branch that compares one value against several possible constant cases, offering a cleaner alternative to a long if-else-if ladder when all the branches test the same variable for equality. Java's switch works on byte, short, char, int (and their wrapper classes), enum types, and String.
Traditional switch statements execute case labels sequentially once a match is found, and — unless a break statement stops execution — control "falls through" into the next case's code. This fall-through behavior is a deliberate design choice inherited from C, useful in specific situations but a frequent source of bugs when forgotten.
Why it matters
A switch states "pick one of these fixed cases" more clearly than a long if-else-if ladder, and the modern form lets the compiler check that you covered every case. The classic form has one famous trap — a forgotten break — that has caused real production bugs, which is why interviewers ask about fall-through so often.
Basic Syntax and Fall-Through
A switch evaluates its expression once, then jumps to the case label whose constant value matches. Execution then continues downward through every subsequent case's statements until it hits a break or reaches the end of the switch block.
- Case labels must be compile-time constants —
finalvariables, literals, or enum constants, never a regular variable or a method call result. - A
defaultcase is optional and runs when no other case matches; it can appear anywhere in the block, though convention places it last. - Multiple case labels can be stacked with no code between them to share one block, which intentionally uses fall-through as a grouping technique.
- Forgetting
breakat the end of a case causes execution to continue into the next case unintentionally — the most common switch bug.
Switching on String and Enum
Since Java 7, switch can compare a String expression against String literal cases, which internally relies on .equals() and hashCode() rather than reference comparison. Switching on an enum is especially clean because you write only the bare constant name in each case label (not Enum.CONSTANT), and the compiler already knows the type from the switch expression.
Modern Enhanced Switch (Java 14+)
Newer Java versions introduced an arrow-based switch expression: case value -> result;. Each arrow case runs only its own branch with no fall-through at all, the switch itself can produce a value directly (assignable to a variable), and a block body can return a value with yield. This form is safer for beginners because it removes the fall-through pitfall entirely, though the classic colon-and-break form remains widely used in existing codebases and is still essential to understand.
Watch: How Java's Enhanced Switch Eliminates Bugs
A quick video on how the arrow-style switch removes fall-through bugs and lets the compiler catch missing cases.
How Enhanced Switch Eliminates Bugs
The enhanced switch isn't just shorter syntax. It turns several bugs that the classic switch lets through silently into bugs the compiler rejects, or makes them impossible to write in the first place:
- No accidental fall-through — an arrow case runs only its own expression or block, so a missing
breakcan never leak execution into the next case. - Exhaustiveness is checked — a switch expression must handle every possible value. For an enum, covering every constant is enough (no
defaultneeded), and if someone later adds a new constant, every switch expression that doesn't handle it stops compiling instead of quietly doing nothing. - No "forgot to assign" paths — because the switch produces the value itself, you assign the variable once (
String type = switch (...) {...};) instead of assigning it separately in each case and hoping none was missed. - Duplicate-looking cases become one line —
case 1, 2, 3 ->replaces stacked empty labels, so there is no fall-through left to misread. - yield makes the result explicit — when a case needs several statements, a
{ ... }block ends withyield value;, and the compiler errors if a block can finish without yielding.
Examples
Classic switch using grouped case labels and break to avoid fall-through
public class DayTypeDemo {
public static void main(String[] args) {
int day = 6;
String type;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
type = "Weekday";
break;
case 6:
case 7:
type = "Weekend";
break;
default:
type = "Invalid";
}
System.out.println(type);
}
}
Weekend
The same logic written as a modern arrow-based switch expression
public class SwitchExpressionDemo {
public static void main(String[] args) {
int day = 3;
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid";
};
System.out.println(type);
}
}
Weekday
The fall-through bug: a missing break grants every access level below the match
public class FallThroughBug {
public static void main(String[] args) {
int level = 1;
switch (level) {
case 1:
System.out.println("Read access");
// missing break -> falls into case 2 and case 3
case 2:
System.out.println("Write access");
case 3:
System.out.println("Admin access");
break;
default:
System.out.println("No access");
}
}
}
Read access
Write access
Admin access
Enhanced switch over an enum: exhaustive, no default, and the compiler catches a missing constant
public class TrafficLightDemo {
enum Light { RED, YELLOW, GREEN }
static String action(Light light) {
// Every constant is covered, so no default is needed.
// Add BLUE to the enum and this method stops compiling
// until a "case BLUE ->" is added.
return switch (light) {
case RED -> "Stop";
case YELLOW -> "Slow down";
case GREEN -> "Go";
};
}
public static void main(String[] args) {
for (Light light : Light.values()) {
System.out.println(light + " -> " + action(light));
}
}
}
RED -> Stop
YELLOW -> Slow down
GREEN -> Go
Using yield when a case needs more than one statement
public class GradeDemo {
public static void main(String[] args) {
int score = 76;
String grade = switch (score / 10) {
case 10, 9 -> "A";
case 8 -> "B";
case 7 -> {
String g = "C";
if (score % 10 >= 5) {
g = "C+";
}
yield g;
}
default -> "F";
};
System.out.println(grade);
}
}
C+
Common Mistakes
- Forgetting "break" at the end of a case in a classic switch, causing execution to silently fall through into the next case.
- Mixing colon cases (case 1:) and arrow cases (case 1 ->) in the same switch, which does not compile — pick one style per switch.
- Using return inside a switch expression block to produce its value; a switch expression block must use yield, not return.
- Adding a default to an enum switch expression that already covers every constant, which hides the compile error you would otherwise get when a new constant is added.
- Trying to use a non-constant variable or a method call result as a case label, which fails to compile.
- Assuming a switch on String is case-insensitive — case labels must match the switch value exactly, including capitalization.
- Writing an enum case label as "Day.MONDAY" instead of just "MONDAY", which does not compile inside a switch on that enum type.
Exercise
Write a method that returns the number of days in a month (ignoring leap years) using a switch expression with arrow syntax. February has 28 days; April, June, September and November have 30; all others have 31.
Expected output: 28, 30, 31, each on its own line. This needs Java 14 or newer.
public class DaysInMonth {
static int daysIn(int month) {
// TODO: return the number of days using a switch expression
return 0;
}
public static void main(String[] args) {
System.out.println(daysIn(2));
System.out.println(daysIn(4));
System.out.println(daysIn(1));
}
}
Show hints
- One case label can list several values separated by commas:
case 4, 6, 9, 11 ->. - A switch expression produces a value, so you can write
return switch (month) { ... };.
Show solution
public class DaysInMonth {
static int daysIn(int month) {
return switch (month) {
case 2 -> 28;
case 4, 6, 9, 11 -> 30;
default -> 31;
};
}
public static void main(String[] args) {
System.out.println(daysIn(2));
System.out.println(daysIn(4));
System.out.println(daysIn(1));
}
}
Quick quiz
1. In a classic switch, what happens when a matching case has no break?
- A. The switch ends
- B. Execution continues into the next case
- C. The code does not compile
- D. The default case runs instead
Show answer
B. Without break, execution falls through into the following case labels until it meets a break or the end of the switch.
2. Which type can NOT be used as the selector of a classic switch?
- A. int
- B. String
- C. long
- D. An enum
Show answer
C. A classic switch accepts byte, short, char, int, their wrappers, String and enums. long, float, double and boolean are not allowed.
3. A switch expression over an enum has no default and misses one constant. What happens?
- A. It returns null for the missing constant
- B. It throws an exception at runtime
- C. It returns 0
- D. It does not compile
Show answer
D. A switch expression must be exhaustive. With an enum, the compiler reports an error if a constant is not covered and there is no default.
Interview questions
What is the difference between a switch statement and a switch expression?
Show answer
A switch statement only performs actions and, in its classic colon form, falls through unless you write break. A switch expression (standard since Java 14) produces a value, so it can be assigned or returned.
With arrow labels there is no fall-through, a case can list several values, and the expression must be exhaustive, so the compiler tells you when a case is missing.
How does a switch on a String compare values, and what happens if the String is null?
Show answer
It compares using the string's content, as equals() would, and it is case-sensitive: "yes" does not match "YES". If the selector is null, the switch throws a NullPointerException, so check for null before switching on a value that might be missing.
Key Points to Remember
- Classic switch falls through to the next case unless a break statement stops it — this is by design, not a bug.
- Case labels must be compile-time constants; switch supports byte/short/char/int, their wrappers, String, and enums.
- The Java 14+ arrow syntax (case value -> result;) has no fall-through and can directly produce a value.
- A switch expression must be exhaustive: over an enum, covering every constant is enough, and a newly added constant becomes a compile error rather than a silent bug.
- Use yield (not return) to produce the value from a multi-statement block inside a switch expression.
- default is optional but should normally be included to handle unexpected values explicitly — except in an enum switch expression that already covers every constant, where leaving it out keeps the compiler's exhaustiveness check working for you.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering