Java Control Flow
A thorough guide to every control flow mechanism in Java: conditionals, all three forms of the switch, every loop type, and the transfer statements that redirect execution. These are the tools that give a program its logic.
if Statement
The if statement is the most fundamental decision-making construct in Java. It evaluates a boolean expression and executes its body only when that expression is true. The condition must be a genuine boolean value, not a numeric one. Unlike C or Python, Java does not treat 0 as false or any non-zero number as true: the compiler will refuse to compile if (1) or if (someInt).
Syntax rules
The condition must be wrapped in parentheses. The body is a block enclosed in braces, or a single statement without braces.
- The parentheses around the condition are mandatory: if (x > 0) not if x > 0.
- Omitting braces for a single-statement body is legal but risky. A future edit that adds a second line will not automatically be included in the if body.
- The near-universal Java convention is to always use braces, even for one-line bodies.
- Watch for the accidental assignment bug: if (x = 5) compiles because the result of an assignment is the assigned value, but it always evaluates to true and reassigns x.
if Statement
JavaBasic condition check with a boolean expression.
if-else Statement
The else block gives you a path for when the condition is false. Every time the program reaches an if-else, exactly one of the two branches will execute. The else block has no condition of its own: it runs whenever the if condition evaluates to false.
if-else Statement
JavaTwo mutually exclusive paths based on a single condition.
else-if Ladder
When you have more than two possible outcomes, you chain additional conditions using else if. Java evaluates each condition in order, from top to bottom. As soon as one condition is true, its block runs and the rest of the chain is skipped entirely. The optional final else acts as a catch-all for any case not covered by the preceding conditions.
Order matters
Because evaluation stops at the first true condition, the order you write the branches directly affects the result.
- Put the most specific or most restrictive conditions first.
- A classic mistake with grade boundaries: checking score >= 60 before score >= 90 means the 90+ range never gets reached because it satisfies the first condition too.
- If none of the conditions is true and there is no final else, nothing executes. Consider whether a default action is needed.
else-if Ladder
JavaGrade classification using a chain of conditions evaluated top to bottom.
Nested if-else
Nesting places one if-else block inside the body of another. This is appropriate when a second decision only needs to be made after a first condition has already been confirmed true. Deep nesting (more than two or three levels) is a code smell: it usually means the logic can be restructured using guard clauses, early returns, or a different control structure entirely.
The dangling else problem
In Java, an else always binds to the nearest preceding if that does not already have an else. Indentation does not determine the binding. Braces do.
- Without braces, if (a) if (b) doX(); else doY(); is parsed as if (a) { if (b) doX(); else doY(); }
- The else is paired with the inner if, not the outer one. This surprises many beginners.
- The fix is always explicit braces, which remove all ambiguity.
Nested if-else
JavaA two-level decision: first check eligibility, then check the category within eligibility.
switch Statement (Traditional)
The traditional switch statement is an alternative to a long else-if ladder when you are comparing a single variable against a set of constant values. It can be more readable and marginally faster in some JVM implementations. The supported types are byte, short, int, char, String, and enum. Notably, you cannot switch on long, float, or double.
Fall-through behaviour
The most important thing to understand about the traditional switch is what happens when you omit break.
- Without a break at the end of a case, execution falls through into the next case block, regardless of whether the next case label matches.
- Fall-through is occasionally intentional: multiple case labels with no body between them share the same handler.
- Accidental fall-through is a common source of bugs. Most static analysis tools warn about it.
- The default case can appear anywhere in the switch, not just at the end, though placing it at the end is the standard convention.
Traditional switch Statement
JavaDay-of-week classifier with intentional fall-through for the weekend cases.
switch Expression (Java 14+)
Java 14 made switch expressions a permanent language feature. They address the two biggest complaints about the traditional switch: the verbosity of writing break on every case, and the inability to use the switch result as a value. The arrow syntax -> replaces the colon-and-break pattern, does not fall through, and can either execute a statement or yield a value that gets assigned to a variable. For multi-statement cases, use a block with yield to return the value from within the block.
switch expression vs. switch statement
The two forms can coexist in the same codebase. Choose based on whether you need a value back.
- Arrow labels (->) :No fall-through. Each arm handles its case and stops. Multiple constants can share an arm: case 6, 7 -> "Weekend".
- yield keyword:Used inside a block arm to return a value from the switch expression. Analogous to return inside a method.
- Exhaustiveness:When used as an expression (assigned to a variable), the switch must cover every possible value or include a default. The compiler enforces this for enums.
switch Expression (Java 14+)
JavaArrow syntax with no fall-through and a yield inside a block arm for complex logic.
Pattern Matching in switch (Java 21+)
Java 21 made pattern matching in switch a permanent feature. It extends switch to work on any type, not just the handful of types the traditional switch supports. Each case can now test both the type and optional conditions of the switched value at the same time. This replaces long chains of instanceof checks followed by casts, consolidating type dispatch into a single, readable block. A guarded pattern adds a when clause to further filter within a type match.
What pattern matching switch replaces
Before Java 21, dispatching on an object's type required a cascade of if-instanceof-cast blocks.
- Old style:if (obj instanceof Integer i) { ... } else if (obj instanceof String s) { ... } else if (...), verbose and hard to maintain.
- New style:switch (obj) { case Integer i -> ...; case String s -> ...; case null -> ...; }, concise and exhaustive.
- Guarded patterns:case Integer i when i > 0 -> ... adds a condition on top of the type check in a single arm.
- null handling:Pattern matching switch can include a case null arm. The traditional switch throws NullPointerException if the value is null.
Pattern Matching in switch (Java 21+)
JavaType patterns and guarded patterns replacing a chain of instanceof checks.
for Loop
The for loop is the right tool when you know exactly how many times the body should execute, or when you need an index variable to track position. Its three-part header keeps all the loop control in one place: initialisation, condition, and update. All three parts are optional, though omitting the condition creates an infinite loop that you will need to exit with break.
for loop structure
for (initialisation; condition; update), each part has a specific role.
- Initialisation:Runs once before the first iteration. Typically declares and sets the loop counter: int i = 0.
- Condition:Evaluated before each iteration. The loop runs as long as this is true. Checked before the body runs, so a false condition from the start means the body never executes.
- Update:Runs after each iteration, before the condition is evaluated again. Typically increments or decrements the counter: i++ or i--.
- Scope:A variable declared in the initialisation (int i = 0) is scoped to the for loop only. It is not accessible after the closing brace.
for Loop
JavaForward counting, backward counting, and stepping by two.
Enhanced for Loop (for-each)
The enhanced for loop, also called the for-each loop, is the clean way to iterate over every element in an array or any collection that implements Iterable. It removes the index variable from the picture entirely, which eliminates a whole class of off-by-one errors. The trade-off is that you lose access to the index: if you need to know the position of the current element, use a regular for loop instead.
Limitations of the enhanced for loop
It covers the common case cleanly but cannot do everything a regular for loop can.
- You cannot modify the array or collection while iterating. Adding or removing elements from a List during a for-each throws ConcurrentModificationException.
- You cannot access the index of the current element directly.
- You cannot iterate in reverse.
- The loop variable is a copy for primitive types. Assigning to it inside the loop does not change the original array element.
Enhanced for Loop
JavaIterating over an int array and a String array without managing an index.
Nested for Loops
A nested loop is a loop placed inside the body of another loop. For every single iteration of the outer loop, the inner loop runs completely from start to finish. This makes nested loops the natural tool for working with two-dimensional data like matrices and grids, or for generating combinations of values. The time complexity grows multiplicatively: two nested loops of size n give you n squared iterations, so keep this in mind with large datasets.
Nested for Loops
JavaPrinting a multiplication table using two nested loops.
while Loop
The while loop is the right choice when you do not know how many iterations are needed in advance. The condition is checked before each iteration, so if it is false from the very beginning, the body never runs. A common pattern is reading input until the user provides a sentinel value, or polling a state until it changes.
Avoiding infinite loops
Every while loop must have a way to eventually make the condition false, or a break statement inside.
- Ensure the loop body contains statements that move the program toward the exit condition.
- A common mistake: forgetting to update the variable being checked in the condition.
- while (true) is a legitimate pattern when paired with break, but only use it when the exit condition is more naturally expressed inside the body.
while Loop
JavaRepeated validation: keep asking until the user enters a value in the expected range.
do-while Loop
The do-while loop is the mirror of the while loop. The condition is checked at the end, after the body has executed. This guarantees that the body runs at least once, regardless of the condition. The classic use case is a menu: you always want to display the menu at least once before you know what the user wants to do.
do-while vs. while
The only difference is the position of the condition check, but that one difference determines the minimum number of iterations.
- while:Condition checked first. Body may never run if condition is initially false.
- do-while:Body runs first. Condition checked after. Body runs at least once unconditionally.
- The semicolon after the closing parenthesis of do-while is mandatory: while (condition);
do-while Loop
JavaA menu that always displays at least once and repeats until the user chooses to exit.
break Statement
The break statement exits the innermost enclosing loop or switch block immediately. Execution resumes at the statement right after the closing brace of the exited structure. It is most useful for exiting a loop early as soon as a search succeeds, or for stopping iteration once a condition is met without needing to restructure the entire loop.
break Statement
JavaLinear search that exits the loop the moment the target is found.
continue Statement
The continue statement skips the rest of the current iteration and jumps directly to the next one. In a for loop, the update expression still runs after continue. In a while or do-while loop, control jumps back to the condition. The loop itself does not exit: only the current iteration is cut short. Use it to filter out values you do not want to process, keeping the main body of the loop focused on the happy path.
continue Statement
JavaProcessing only odd numbers by skipping even values with continue.
Labeled break and continue
By default, break and continue apply to the innermost loop. Labels let you target an outer loop directly. A label is an identifier followed by a colon, placed on the line before the loop statement it names. Labeled breaks and continues are not common in everyday code, but they are genuinely useful in nested loop algorithms where the alternative is a messy combination of boolean flags to propagate a break condition upward.
Labels are not goto
Java has a goto keyword reserved but not implemented. Labels with break and continue are the controlled alternative.
- Labels only work with break and continue. You cannot jump to an arbitrary label anywhere in your code.
- The label must be attached to a loop or a block that directly or indirectly contains the break or continue statement.
- Excessive use is a sign that the algorithm might benefit from being broken into smaller methods instead.
Labeled break and continue
JavaBreaking out of the outer loop from inside an inner loop, and continuing the outer loop from inside an inner one.
return Statement
The return statement ends the execution of a method and optionally sends a value back to the caller. In a method declared with a return type, you must return a value of that type from every possible code path. In a void method, return; (with no value) is optional at the end but can be used earlier to exit the method based on a condition. This technique of returning early when a precondition fails is called a guard clause, and it reduces nesting significantly.
Guard clauses vs. deep nesting
Returning early when inputs are invalid keeps the main logic of a method at a consistent indentation level.
- Nested style:if (valid) { if (another condition) { main logic } }, deeply nested, hard to follow.
- Guard clause style:if (!valid) return; if (!another condition) return; then the main logic at the top level.
- Guard clauses make the normal path of a method easy to read because it is not buried inside conditions.
- The compiler verifies that every non-void method returns a value on every path. Missing a return in a branch is a compile-time error.
return Statement and Guard Clauses
JavaEarly returns to handle edge cases, keeping the main logic clean and at a flat indentation level.
Quiz - Test Your Knowledge
Ten questions covering conditionals, switch forms, loops, and transfer statements. Read each option carefully before selecting your answer.
Knowledge Check
1. Which of the following correctly demonstrates an else-if ladder?
2. What types can a traditional switch statement in Java (before Java 14) evaluate?
3. In a traditional switch statement, what happens if you omit the break statement at the end of a case?
4. What is the key syntactic difference of a switch expression (Java 14+) compared to a traditional switch statement?
5. What is the difference between a while loop and a do-while loop?
6. What does the continue statement do inside a loop?
7. Which loop is best suited when you know the number of iterations in advance?
8. What does a labeled break do that an unlabeled break cannot?
9. Which of the following is a valid enhanced for loop for an int array called numbers?
10. What is the result of executing return in a void method?