Java Control Flow β€” if, switch, for, while, break, continue

Control flow is how a Java program decides which code runs and how often. Java has eight constructs: if/else, switch (statement and expression), three loops (for, while, do-while), the enhanced for-each, and the jump statements break, continue and return.

if / else if / else

if (score >= 90)        grade = 'A';
else if (score >= 80)   grade = 'B';
else if (score >= 70)   grade = 'C';
else                    grade = 'F';

The condition must be a boolean β€” Java doesn't allow if (count) like C or JavaScript.

switch β€” statement vs expression

Pre-Java 14, switch was statement-only with break needed to avoid fall-through. Modern Java has the expression form with arrow syntax β€” no fall-through, returns a value:

// Modern (Java 14+) β€” switch expression
String label = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
    case SATURDAY, SUNDAY                              -> "Weekend";
};

// Classic β€” switch statement
switch (day) {
    case MONDAY: case TUESDAY: case WEDNESDAY:
    case THURSDAY: case FRIDAY:
        label = "Weekday"; break;
    case SATURDAY: case SUNDAY:
        label = "Weekend"; break;
}

Pattern matching for switch (Java 21+)

String describe(Object o) {
    return switch (o) {
        case Integer i when i > 0 -> "positive int " + i;
        case Integer i            -> "non-positive int";
        case String s             -> "string of length " + s.length();
        case null                 -> "null";
        default                   -> "something else";
    };
}

Loops

// Classic for β€” when you need the index
for (int i = 0; i < list.size(); i++) { ... }

// Enhanced for / for-each β€” when you don't
for (User u : users) { send(u); }

// while β€” condition checked first
while (queue.isEmpty() == false) { process(queue.poll()); }

// do-while β€” body runs at least once
do { input = readLine(); } while (input.isEmpty());

break, continue and labels

outer:
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        if (grid[i][j] == target) {
            found = true;
            break outer;     // exits BOTH loops
        }
    }
}

Labelled break is the cleanest way out of nested loops. Avoid labels in any other situation β€” they make code hard to follow.

Streams aren't control flow β€” but they often replace it

// Loop + accumulator
int sum = 0;
for (Order o : orders) if (o.isPaid()) sum += o.total();

// Stream equivalent (Java 8+)
int sum = orders.stream()
    .filter(Order::isPaid)
    .mapToInt(Order::total)
    .sum();

All sub-topics

Common mistakes

  • Assignment instead of comparison: if (x = 5) β€” Java catches this for non-boolean types but not for boolean.
  • Fall-through in classic switch: forgetting break is the #1 switch bug. The arrow form (Java 14+) eliminates it.
  • Off-by-one in for: i <= list.size() is one too many. Use i < list.size().
  • Modifying a collection during for-each: throws ConcurrentModificationException. Use an explicit Iterator with .remove(), or collect to a new list.

Try it & related tools

Test any control-flow snippet in the Java Online Compiler. For loop performance and timing, the Timestamp tool shows how to measure with System.nanoTime().