break / continue / labeled Statements

break exits a loop (or switch) entirely, continue skips to the next iteration, and labels let both reach past nested loops — powerful tools that are easy to overuse into unreadable control flow.

break and continue give you fine-grained control over loop execution beyond what the loop's own condition provides: break exits a loop immediately, and continue skips the rest of the current iteration and moves to the next one. Labeled statements extend both to target an outer loop from inside a nested one.

break: Exiting a Loop Immediately

break immediately terminates the nearest enclosing loop (or switch statement), and execution continues with the first statement after that loop.

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;
    }
    System.out.println(i);
}
System.out.println("after loop");
// prints 0 1 2 3 4, then "after loop" - the loop never reaches i == 5's println

continue: Skipping to the Next Iteration

continue skips the rest of the current iteration's body and jumps straight to the loop's next iteration check — for a for loop, that means the update clause runs before the condition is re-checked; for while/do-while, it jumps straight to the condition check.

In a for loop, continue does NOT skip the update clause — i++ still runs. Only the code between continue and the closing brace of the body is skipped.

for (int i = 0; i < 6; i++) {
    if (i % 2 == 0) {
        continue; // skip even numbers
    }
    System.out.println(i);
}
// prints 1 3 5

break vs continue: Side by Side

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break; // stop the loop completely
    }
    System.out.println("break demo: " + i);
}
// break demo: 1
// break demo: 2

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue; // skip just this one iteration
    }
    System.out.println("continue demo: " + i);
}
// continue demo: 1
// continue demo: 2
// continue demo: 4
// continue demo: 5

break in switch Statements

break also stops execution from falling through to the next case in a traditional switch statement — this is a separate use of the same keyword, unrelated to loops.

int day = 3;
switch (day) {
    case 1:
        System.out.println("Mon");
        break;
    case 3:
        System.out.println("Wed");
        break; // without this, execution would fall into case 4's code
    default:
        System.out.println("Other");
}

Nested Loops: Where break and continue Fall Short

In a nested loop, a plain break or continue only affects the innermost loop that directly contains it — it cannot, by itself, exit or skip an outer loop.

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) {
            break; // only exits the inner (j) loop
        }
        System.out.println(i + "," + j);
    }
}
// 0,0  1,0  2,0  - the outer loop keeps running all three times

Labeled Statements

A label is an identifier followed by a colon, placed immediately before a loop. break label; and continue label; then target that specific labeled loop, no matter how deeply nested the break/continue itself is.

outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) {
            break outer; // exits BOTH loops entirely
        }
        System.out.println(i + "," + j);
    }
}
// prints only: 0,0

Labeled continue

continue label; skips the rest of the current iteration of the labeled loop — including any remaining inner-loop work — and moves that outer loop on to its next iteration.

outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) {
            continue outer; // abandon the inner loop, move outer loop on
        }
        System.out.println(i + "," + j);
    }
}
// 0,0  1,0  2,0

Labeling for Loop Selection, Not Just the Outermost

A label can be placed on any loop, and a break/continue can target any labeled loop it's lexically inside — not necessarily the outermost one. This is useful in three-or-more levels of nesting where you want to escape only partway out.

middle:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        for (int k = 0; k < 3; k++) {
            if (k == 1) {
                continue middle; // skips remaining j and k work, advances i
            }
            System.out.println(i + "," + j + "," + k);
        }
    }
}

Practical Usage

Common uses: searching a 2D grid and stopping as soon as a match is found (labeled break), skipping invalid rows while validating a matrix (labeled continue), and early-exiting a loop once a result is known so the rest of the work is skipped.

int[][] grid = {{1, 2}, {3, 4}, {5, 6}};
int target = 4;
boolean found = false;

search:
for (int[] row : grid) {
    for (int value : row) {
        if (value == target) {
            found = true;
            break search; // no need to keep scanning once found
        }
    }
}
System.out.println("Found: " + found);

Readability Considerations

Labeled break/continue are legal and occasionally the clearest option, but overusing them (or nesting many labels) can make control flow hard to follow — a reader has to hunt for the label to know what actually stops. Prefer extracting a helper method (using return to exit early) or a boolean flag when it makes the intent clearer, and reserve labels for cases where restructuring would be more awkward than the label itself, such as breaking out of a genuinely multi-level search.

// Often clearer than a labeled break: extract a method and use return
static boolean containsTarget(int[][] grid, int target) {
    for (int[] row : grid) {
        for (int value : row) {
            if (value == target) {
                return true; // exits the method immediately, no labels needed
            }
        }
    }
    return false;
}

Edge Cases

A label with no matching break/continue referencing it is legal but pointless. A break or continue cannot reference a label that isn't lexically enclosing it (you can't jump into or across unrelated loops) — that's a compile-time error, keeping labeled jumps confined to genuine nesting relationships rather than arbitrary gotos.

break and continue (labeled or not) are only valid inside a loop (continue) or inside a loop/switch (break) — using either outside such a context is a compile error.

Complete Example

The labeled continue lets the inner divisor-checking loop abandon its work as soon as a factor is found, cleanly moving the outer loop on to the next candidate.

public class PrimeFinder {
    public static void main(String[] args) {
        int limit = 30;
        candidates:
        for (int n = 2; n <= limit; n++) {
            for (int divisor = 2; divisor * divisor <= n; divisor++) {
                if (n % divisor == 0) {
                    continue candidates; // n is not prime, move to the next n
                }
            }
            System.out.println(n + " is prime");
        }
    }
}

Quick Reference

break;              exit the nearest enclosing loop or switch
continue;           skip to the next iteration of the nearest enclosing loop

label:
for (...) { ... }   defines a label immediately before a loop

break label;        exit the labeled loop, however deeply nested inside it
continue label;     skip to the next iteration of the labeled loop

Key Takeaways

break exits the nearest enclosing loop (or switch) entirely; continue skips only the rest of the current iteration.

In a for loop, continue still lets the update clause run before the next condition check.

Plain break/continue only affect the innermost loop they're directly inside — labels are needed to target an outer loop.

A label is placed immediately before a loop and referenced as break label; or continue label;.

Labels are legal and sometimes clearest for multi-level search/skip logic, but overuse hurts readability — consider extracting a method with an early return as an alternative.

for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { break; // intended to exit both loops } } } outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (j == 1) { break outer; // actually exits both loops } } }

A plain break only exits the innermost loop it's directly inside; to exit an outer loop from inside a nested one, the outer loop needs a label and the break must reference it.

switch (grade) { case 'A': System.out.println("Excellent"); case 'B': System.out.println("Good"); } switch (grade) { case 'A': System.out.println("Excellent"); break; case 'B': System.out.println("Good"); break; }

Without break, a traditional switch statement falls through into the next case's code even when its label doesn't match, so grade == 'A' would print both "Excellent" and "Good".

for (int i = 0; i < 5; i++) { if (i == 2) { break; } continue; // pointless here, sits at the very end of the body anyway } for (int i = 0; i < 5; i++) { if (i == 2) { break; } // do the real work here }

A continue placed as the last statement in a loop body has no effect since the iteration was about to end anyway — it adds noise without changing behavior.

label1: for (int i = 0; i < 3; i++) { } //... continue label1; // outside the loop entirely - compile error label1: for (int i = 0; i < 3; i++) { if (i == 1) { continue label1; // valid: inside the labeled loop } }

A labeled break or continue must appear lexically inside the loop the label refers to; referencing a label from outside that loop's body is a compile-time error, not a jump to arbitrary code.

What is the difference between what break and continue do to a loop?

break terminates the loop entirely and control moves to the code after it; continue skips only the remaining statements in the current iteration and moves on to the loop's next iteration check.

In a for loop, does continue skip the update clause (like i++)?

No — continue skips the rest of the loop body for that iteration, but the for loop's update clause still runs before the condition is checked again.

Why doesn't a plain, unlabeled break inside a nested loop exit both loops?

An unlabeled break only affects the nearest enclosing loop — the one it's directly written inside — so in a nested loop it only exits the innermost loop, leaving the outer loop to continue.

How do you make a break statement exit an outer loop from inside a nested inner loop?

Place a label immediately before the outer loop's declaration (e.g. outer:), then use break outer; inside the inner loop to exit all the way out to after the outer loop.

What is a legitimate alternative to a labeled break for exiting nested loops early, and why might it be preferred?

Extracting the nested loops into a separate method and using an early return to exit as soon as the result is known; it's often preferred because a return's exit point is explicit at the method boundary, which many readers find clearer than hunting for a label.

Can break or continue be used outside of any loop (or, for break, outside a switch)?

No — both are only valid inside a loop (continue), or inside a loop or switch statement (break); using either outside such a context is a compile-time error.

What happens if a label is defined but no break or continue ever references it?

Nothing goes wrong — it's legal, just pointless; the loop behaves exactly as if the label weren't there.

Write code that searches a 2D array of integers for the first negative number and prints its row and column, stopping the search immediately once found (using a labeled break).

int[][] grid = {{4, 7, 2}, {9, -3, 1}, {5, 6, 8}}; search: for (int r = 0; r < grid.length; r++) { for (int c = 0; c < grid[r].length; c++) { if (grid[r][c] < 0) { System.out.println("Found at (" + r + "," + c + ")"); break search; } } }

The labeled break search exits both the row loop and the column loop as soon as a negative value is found, avoiding wasted iterations over the remaining grid once the answer is already known.

What does this print (trace it through)?

1 3 — i=1 prints (odd, not 5); i=2 is even, continue skips it; i=3 prints; i=4 is even, continue skips it; i=5 is odd but hits the break before printing, so the loop stops. Final output: "1 3 ".

Write a nested loop over a 3x3 grid that skips printing whenever row equals column (the diagonal), using continue (no label needed since it's the same loop level).

for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { if (row == col) { continue; } System.out.println(row + "," + col); } }

The continue skips only the current inner-loop iteration when row equals col, so the diagonal cells (0,0), (1,1), (2,2) are never printed, while every other cell is.

What does this print, and why does the inner loop never print anything for j values after i==j is hit?

01 02 10 12 20 21 — whenever i equals j, continue outer abandons the rest of the inner loop immediately (never printing pairs like 00, 11, 22, or any later j for that i) and moves straight to the next i, which is exactly what happens for 0,0 / 1,1 / 2,2.

for (int i = 1; i <= 5; i++) {
    if (i % 2 == 0) {
        continue;
    }
    if (i == 5) {
        break;
    }
    System.out.print(i + " ");
}
outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == j) {
            continue outer;
        }
        System.out.print(i + "" + j + " ");
    }
}