JAVA / LOOPS AND ARRAYS
break, continue and labelled control flow
Use break, continue and labels to exit or skip loop iterations exactly when you mean to, including escaping nested loops without flag variables.
What you will learn
- Use continue to skip one iteration and break to end the whole loop statement
- Escape nested loops with a labelled break instead of a boolean flag
- Know that a for loop still runs its update after continue, but a while loop does not
- Recognise that break inside a switch exits the switch, not the enclosing loop
Understanding break, continue and labelled control flow
break and continue are jumps that Java permits inside a loop body (break is also legal inside a switch). break abandons the loop statement itself: control resumes at the first statement after the loop's closing brace and the condition is never tested again. continue is narrower, ending only the current iteration: control jumps to the bottom of the body, then the loop's normal bookkeeping runs, which means a for loop evaluates its update expression and every loop re-tests its condition.
By default both apply to the innermost enclosing loop, which is what makes leaving a pair of nested loops awkward: a plain break in the inner loop just hands control back to the outer loop, which happily starts another iteration. A label fixes that. Write an identifier and a colon immediately before a loop, such as search:, and then break search; finishes that entire loop while continue search; abandons the current iteration of it, throwing away whatever the inner loop had left to do. The label is not a place you jump to; it only names which loop statement the break or continue is talking about.
The alternative to break is a boolean like found that you set in one place and re-test in every loop condition, which spreads a single decision across several lines and is easy to get subtly wrong. break states the exit at the exact point the decision is made, so a reader sees the reason and the effect together. Labels are the rarest of the three in real Java code, because a nested search that lives in its own method can use return instead and needs no label at all; reach for a label when extracting a method would make the code worse, not as your first move.
public class BreakContinueDemo {
public static void main(String[] args) {
int[] readings = {4, -1, 7, 0, 9, -3, 12};
int sum = 0;
for (int r : readings) {
if (r < 0) {
continue; // bad sample: ignore it, keep going
}
if (r == 0) {
break; // 0 is the end-of-data marker
}
sum += r;
System.out.println("added " + r + ", sum=" + sum);
}
System.out.println("final sum " + sum);
}
}break ends the loop statement itself while continue ends only the current iteration, and a label chooses which enclosing loop either one applies to.
Worked examples
Labelled break out of a grid search
Stops both loops the moment a match is found, instead of ending only the inner loop.
public class LabelledSearch {
public static void main(String[] args) {
int[][] grid = {
{3, 8, 1},
{5, 9, 4},
{7, 2, 6}
};
int target = 9;
int foundRow = -1;
int foundCol = -1;
search:
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
System.out.println("checking [" + row + "][" + col + "]");
if (grid[row][col] == target) {
foundRow = row;
foundCol = col;
break search;
}
}
}
System.out.println("found " + target + " at [" + foundRow + "][" + foundCol + "]");
}
}Example explained
Line 1search: names the outer for statement, so break search; knows which loop to finish.
Line 2break search; ends both loops at once, which is why no checking line is printed for [1][2] or for row 2.
Line 3A plain break would end only the inner loop, and the outer loop would then start row 2 even though the answer was already known.
Line 4foundRow and foundCol are declared before the loops because anything declared inside the body is gone once the iteration ends.
Labelled continue to abandon one row
Shows continue on an outer label skipping the rest of the inner loop and the code after it.
public class LabelledContinue {
public static void main(String[] args) {
String[][] rows = {
{"a", "b", "c"},
{"d", "", "e"},
{"f", "g", "h"}
};
outer:
for (int i = 0; i < rows.length; i++) {
StringBuilder line = new StringBuilder();
for (int j = 0; j < rows[i].length; j++) {
if (rows[i][j].isEmpty()) {
System.out.println("row " + i + " has a blank cell, skipping row");
continue outer;
}
line.append(rows[i][j]);
}
System.out.println("row " + i + " -> " + line);
}
}
}Example explained
Line 1continue outer; ends the current iteration of the outer loop, so cell "e" in row 1 is never appended.
Line 2It also skips the println that sits after the inner loop, which is why row 1 never reports a joined line.
Line 3The outer loop still runs i++ and re-tests i < rows.length, so row 2 is processed normally.
Line 4A plain continue would skip only the blank cell and row 1 would print de.
continue and the loop update
Contrasts what continue skips in a for loop with what it skips in a while loop.
public class ContinueAndUpdate {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
if (i % 2 == 0) {
continue;
}
System.out.println("for odd " + i);
}
int j = 0;
while (j < 5) {
int current = j;
j++; // advance before any continue
if (current % 2 == 0) {
continue;
}
System.out.println("while odd " + current);
}
}
}Example explained
Line 1In the for loop, continue skips the println but i++ still runs, because the update belongs to the for statement and not to the body.
Line 2In the while loop, j++ is just an ordinary statement, so it is placed above the continue to guarantee it happens.
Line 3current copies the value under test because j has already moved on by the time the body inspects it.
Line 4Moving j++ below the continue would leave j at 0 forever and the program would never terminate.
Important notes
A label can be attached to any statement, not only a loop, so done: { ... break done; } compiles; continue, however, accepts only a label that names a loop and is a compile error otherwise.
A statement written directly after break or continue in the same block does not compile at all: javac reports "unreachable statement" rather than a warning.
Common mistakes
Putting continue above the i++ in a while loop: the condition keeps testing the same value, so the program hangs instead of finishing.
Writing break inside a switch that sits inside a loop and expecting the loop to stop: the break leaves only the switch, so the loop keeps iterating and a labelled break is needed to reach it.
Reading break outer; as "go back to the outer label and start again": it exits the outer loop completely, so code that meant continue outer; silently drops every remaining row.
Try it yourself
Change, predict, then run
Build a 4x4 int array mixing positive and negative values, use continue to skip negatives, and use a labelled break to print the row and column of the first value divisible by 7 and stop scanning. Then change break search; to continue search; and confirm the scan now reports at most one match per row.
Open the Java workspaceCheck your understanding
Rewriting for (int i = 0; i < 3; i++) as int i = 0; while (i < 3) { ... i++; } turns a working continue into an infinite loop. Why?
- continue is only legal inside a for loop, so the while version silently loops forever
- continue restarts the loop from the top with i reset to 0
- A for loop's update expression is part of the loop statement and still runs on continue, but in the while version i++ is just the last statement of the body, which continue skips
- while loops re-test their condition only when the body completes normally, so continue leaves the previous condition result in place
Show answer
i++ belongs to the for statement itself, so continue jumps past the body but still evaluates it; in the while form the same i++ is ordinary body code and continue jumps over it, leaving i unchanged. The last option is tempting but wrong: continue does re-evaluate the condition every time, and that is exactly the problem, because the value it tests never changed.