Control Flow: Conditions, Loops, and Modern Switch
if/else, the three loop forms, and Java's modern switch expression — including the exhaustiveness checking that catches a forgotten case at compile time.
What you'll learn
- Choose the right loop form (for, while, do-while, for-each) for a given task
- Use a modern switch expression, including pattern matching on sealed cases
- Avoid the classic infinite-loop and off-by-one mistakes
Prerequisites
Explanation
Java has four loop forms, each suited to a different situation. A for loop (for (int i = 0; i < n; i++)) is the right choice when you know the number of iterations, or need an index. A while loop fits when the number of iterations isn't known up front and depends on a condition evaluated before each pass. A do-while loop is the same as while, except the body runs once before the condition is ever checked — useful when a loop must always execute at least once (like "prompt the user, then re-prompt until valid input"). A for-each loop (for (String name : names)) is the idiomatic way to iterate over every element of an array or a Collection when you don't need the index — it's shorter, and it eliminates the entire class of off-by-one bugs that come from hand-managing an index variable.
Traditional switch statements fell through to the next case unless you wrote break — a design that caused countless real bugs from a forgotten break. Modern Java's switch expression (-> arrow syntax, available since Java 14) fixes this: each case runs only its own code, no fall-through, and a switch expression can directly produce a value rather than requiring you to assign inside every branch:
String sizeLabel = switch (itemCount) {
case 0 -> "empty";
case 1 -> "single item";
default -> itemCount + " items";
};
When switching over an enum, the compiler can verify exhaustiveness — if you cover every enum value and add a default, or (since Java 21) cover every case of a sealed type without needing a default at all, a later addition of a new enum constant makes the compiler flag every switch that doesn't yet handle it. That turns a class of bugs that would otherwise only surface at runtime — "we added a new order status and forgot to update this switch" — into a compile error instead, which is a meaningfully stronger safety guarantee than most languages' switch/case gives you.
Example
The same 'no fall-through, produces a value' switch-expression shape, modeled with a lookup-style function.
function sizeLabel(itemCount) {
switch (true) {
case itemCount === 0: return "empty";
case itemCount === 1: return "single item";
default: return itemCount + " items";
}
}
console.log(sizeLabel(0)); // "empty"
console.log(sizeLabel(1)); // "single item"
console.log(sizeLabel(5)); // "5 items"Try it yourself
Add a case for itemCount === 2 that returns "a pair", then verify sizeLabel(2) uses it.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Write sumUpTo(n) that models a Java for-loop computing the sum 1+2+...+n (n >= 0; return 0 if n is 0). Do not use the closed-form formula -- actually loop, the way the Java for loop would.
Checks: sumUpTo(0) is 0 · sumUpTo(1) is 1 · sumUpTo(5) is 15
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write firstNegativeIndex(numbers) modeling a Java for-loop with an early return: return the index of the first negative number in the array, or -1 if none exists. Do not use Array.findIndex -- write the loop by hand to practice the pattern.
Checks: finds a negative in the middle · returns -1 when there are no negatives · finds a negative at index 0 · handles an empty array
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Common mistakes
- Writing `for (int i = 0; i <= n; i++)` when iterating an array by index -- this reads one past the end (ArrayIndexOutOfBoundsException); array loops need i < length, not <=.
- Relying on old-style switch fall-through by forgetting a break -- prefer the -> switch expression form, which has no fall-through at all.
- Writing a while loop whose condition never becomes false because the loop body forgets to update the variable the condition depends on.
Knowledge check
Takeaway
Pick the loop form that matches how the iteration count is actually known (fixed count -> for, condition-driven -> while, must-run-once -> do-while, every element -> for-each), and prefer switch expressions over classic switch statements to eliminate fall-through bugs entirely.
Summary
Java's four loop forms each fit a different shape of iteration. Modern switch expressions (-> syntax) produce a value directly and never fall through between cases, and the compiler can enforce exhaustiveness over enums and sealed types.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.