Control Flow

This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Control-flow statements decide which code runs and how often. This page follows the dev.java "Controlling the Flow of Your Program" and dev.java "Branching with Switch Expressions" tracks; the normative rules are in JLS chapter 14 ("Blocks, Statements, and Patterns") and, for switch expressions, JLS 15.28.

if/else, while, and do/while

if runs a block when a boolean condition is true; the optional else runs otherwise, and else if chains further tests. The condition must be a boolean — Java has no "truthy" values. See The if-then and if-then-else Statements.

int score = 72;

if (score >= 90) {
    System.out.println("A");
} else if (score >= 60) {
    System.out.println("pass");
} else {
    System.out.println("fail");
}

while tests before each iteration, so its body may run zero times; do/while tests after, so the body always runs at least once. See The while and do-while Statements.

int n = 5;
while (n > 0) {
    System.out.print(n + " ");   // 5 4 3 2 1
    n--;
}

int attempts = 0;
do {
    attempts++;
} while (attempts < 3);          // body ran 3 times

A ternary conditional expression cond ? a : b is a value, not a statement — prefer it over a multi-line if/else that only assigns one variable. return inside a method, and break/continue inside a loop, also transfer control (see below).

for, Enhanced for, break, continue, and Labels

The classic for gathers initialization, test, and update on one line; any of the three parts may be omitted, and for (;;) loops forever. See The for Statement.

for (int i = 0; i < 5; i++) {
    System.out.print(i);         // 01234
}

The enhanced for (for-each) iterates the elements of an array or of anything implementing Iterable, without an index variable. Use it whenever you do not need the index.

int[] primes = {2, 3, 5, 7, 11};
int sum = 0;
for (int p : primes) {
    sum += p;                    // 28
}

var names = java.util.List.of("Ada", "Bo", "Cy");
for (var name : names) {         // works for any Iterable
    System.out.println(name);
}

break exits the innermost loop or switch; continue skips to the next iteration. A labeled statement lets either one target an outer loop — the only place a label is meaningful in Java, and its structured stand-in for goto (a reserved word with no function). See Branching Statements.

outer:
for (int row = 0; row < 3; row++) {
    for (int col = 0; col < 3; col++) {
        if (row + col == 3) {
            break outer;         // leaves BOTH loops
        }
        if (col == row) {
            continue outer;      // next row
        }
        System.out.println(row + "," + col);
    }
}

switch Statements

A switch statement transfers control to the case label matching the selector and keeps executing — falling through subsequent cases — until a break, yield, return, or the end of the block. default handles any unmatched value. With traditional colon labels the selector may be an integral type, an enum constant, or a String. See The switch Statement.

enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

Day day = Day.SAT;

String kind;
switch (day) {
    case SAT:
    case SUN:
        kind = "weekend";
        break;                   // WITHOUT this, execution falls into default
    default:
        kind = "weekday";
}
System.out.println(kind);        // weekend

String command = "add";
switch (command) {               // switch on a String
    case "add"    -> System.out.println("adding");   // arrow form: no fall-through
    case "remove" -> System.out.println("removing");
    default       -> System.out.println("unknown");
}

A missing break is a classic bug source. The arrow label () runs exactly one branch and never falls through; prefer it for new code. Colon and arrow labels cannot be mixed in one switch.

switch Expressions

Since Java 14 a switch can be an expression that produces a value (JEP 361). Arrow labels give the result directly; a braced block supplies its value with yield. Several constants can share one label, comma-separated.

int numLetters = switch (day) {
    case MON, FRI, SUN -> 6;
    case TUE           -> 7;
    case THU, SAT      -> 8;
    case WED           -> 9;
};

String plan = switch (day) {
    case SAT, SUN -> "rest";
    default -> {
        int idx = day.ordinal();
        yield "workday #" + (idx + 1);   // yield supplies this block's value
    }
};

A switch expression must be exhaustive — every possible selector value is covered. Switching over an enum with a case for each constant needs no default; the compiler adds a hidden branch that throws MatchException if the enum gains a constant later. For any other selector type you must write default.

jshell> String size = switch (3) { case 1 -> "one"; case 2, 3 -> "few"; default -> "many"; };
size ==> "few"

The selector can also be matched with type patterns, record deconstruction patterns, and when guards — a full treatment is on Pattern Matching.

Object value = 42;
String text = switch (value) {
    case Integer i when i > 0 -> "positive int " + i;
    case Integer i            -> "int " + i;
    case String s             -> "string of length " + s.length();
    case null                 -> "null";
    default                   -> "something else";
};

Statement switch vs. Expression switch

The flowchart contrasts a fall-through switch statement — where each case needs its own break — with the equivalent arrow-form switch expression, which runs one branch and produces one value.

flowchart TB subgraph STATEMENT["switch STATEMENT — colon labels, fall-through"] direction TB a0([selector: Day day]) --> a1{case label matches?} a1 -->|case SAT| a2[kind = weekend] a1 -->|case SUN| a3[no statements of its own] a3 --> a2 a2 --> a4{break present?} a4 -->|yes| a9([resume after switch]) a4 -->|no — falls through| a5[default: kind = weekday] a1 -->|no match| a5 a5 --> a9 end subgraph EXPRESSION["switch EXPRESSION — arrow labels, one value"] direction TB b0([selector: Day day]) --> b1{case label matches?} b1 -->|case SAT, SUN| b2[yield weekend] b1 -->|default| b3[yield weekday] b2 --> b9([value assigned to kind]) b3 --> b9 end

See Also

  • Pattern Matching — type patterns, record patterns, and when guards in switch.

  • Operators and Expressions — the ternary conditional operator and boolean short-circuiting.

  • Arrays — the enhanced for over arrays.

  • Enums — switch over enum constants and exhaustiveness.