Free lesson · Java Basic To Advance
Break Continue
Exit Early, Skip Iterations
Sometimes you want to exit a loop before it would naturally finish. Sometimes you want to skip the rest of the current iteration and move to the next. Java has two keywords for these patterns: break and continue.
break; // exit the enclosing loop immediately
continue; // skip to the next iteration of the enclosing loop
These are useful tools. They're also frequently misused — sometimes badly enough to make code unreadable. This Topic covers when to use them, when to avoid them, and the patterns where each is the right choice.
The break Statement
break exits the innermost enclosing for, while, do-while, or switch. Execution jumps to the statement after the loop:
int[] numbers = {1, 5, 7, 12, 3, 8};
int target = 12;
int foundIndex = -1;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == target) {
foundIndex = i;
break; // exit the loop immediately
}
}
System.out.println("Found at: " + foundIndex);
The loop exits as soon as the target is found. Without break, the loop would continue checking the remaining elements, wasting time after we've already found what we need.
Common break patterns:
Searching:
String found = null;
for (String item : items) {
if (matches(item)) {
found = item;
break;
}
}
Validation with early failure:
boolean allValid = true;
for (Input input : inputs) {
if (!input.isValid()) {
allValid = false;
break;
}
}
Worklist termination:
while (!queue.isEmpty()) {
Task task = queue.poll();
if (task.isFatal()) {
break; // stop processing on fatal errors
}
process(task);
}
In each case, break is exiting because we've determined that further iteration is unnecessary.
Interactive animation: Searching for 12 and stopping the moment it is found - break exits the loop and prints Found at: 3.
The continue Statement
continue skips the rest of the current iteration and jumps to the next one. For a for loop, that means running the update expression and re-checking the condition. For while/do-while, it re-checks the condition:
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // skip even numbers
}
System.out.println(i);
}
// Prints 1, 3, 5, 7, 9
Common continue patterns:
Filtering:
for (User user : users) {
if (!user.isActive()) {
continue; // skip inactive users
}
process(user);
}
Skipping invalid input:
for (String line : lines) {
if (line.isEmpty() || line.startsWith("#")) {
continue; // skip blanks and comments
}
parseLine(line);
}
The continue here makes the loop body's main logic (parseLine) unindented and easy to see. Without continue, you'd need:
for (String line : lines) {
if (!line.isEmpty() && !line.startsWith("#")) {
parseLine(line);
}
}
Either works. Continue often reads cleaner when there are multiple skip conditions — each can be its own if + continue, rather than ANDing them all together.
graph TD
A[Loop iteration starts] --> B[Execute body]
B --> C{break?}
C -->|yes| D[Exit loop]
C -->|no| E{continue?}
E -->|yes| F[Jump to update]
E -->|no| G[Continue body]
G --> H[Reach end of body]
H --> F
F --> I[Update expression]
I --> J{Condition still true?}
J -->|yes| A
J -->|no| D
style C fill:#dc2626,color:#fff
style E fill:#f59e0b,color:#fff
style D fill:#7c3aed,color:#fff
This diagram shows how break and continue affect loop flow. When the loop body executes, two control-flow statements can intervene. Break exits the loop entirely, shown by jumping immediately to the exit. Continue skips the rest of the current iteration, jumping to the update expression and then checking the condition again. If neither break nor continue fires, the body runs to completion and continues with the next iteration normally. These two statements give you fine-grained control over loop execution.
Interactive animation: continue as a guard clause beside nested conditions - the filters stay at the top and the work stays flat.
When break and continue Improve Code
A few patterns where these statements genuinely improve readability:
Early exit prevents nested indentation:
// Without break — every condition must be checked even after we know we won't process
for (Item item : items) {
if (item.isReady() && !item.isExpired() && item.hasPermission()) {
process(item);
}
}
// With early continue — easier to extend, each check is its own line
for (Item item : items) {
if (!item.isReady()) continue;
if (item.isExpired()) continue;
if (!item.hasPermission()) continue;
process(item);
}
The version with continues is sometimes called "guard clauses" — each check filters out one case. As you add more conditions, this style scales better than nested if statements.
Short-circuit search:
// Without break — checks every element even after finding
boolean found = false;
for (String item : items) {
if (item.equals(target)) {
found = true;
}
}
// With break — stops as soon as we find
boolean found = false;
for (String item : items) {
if (item.equals(target)) {
found = true;
break;
}
}
For large collections, the version with break can be much faster.
When break and continue Hurt Code
A few cases where they're sometimes overused:
Excessive breaks in long loops:
for (Item item : items) {
if (condition1) break;
process1(item);
if (condition2) break;
process2(item);
if (condition3) break;
process3(item);
}
When a loop has many exit points, it's hard to reason about. Consider whether the logic could be a state machine, a method with returns, or just simpler.
Breaks inside complex conditional logic:
for (Item item : items) {
if (someCondition) {
if (otherCondition) {
if (anotherCondition) {
break;
}
}
}
}
Nested breaks become impossible to track. Combine the conditions with && or extract a method.
Using continue when an if would be clearer:
// continue feels heavy here
for (User user : users) {
if (!user.isActive()) continue;
process(user);
}
// Sometimes the if version reads just as well
for (User user : users) {
if (user.isActive()) {
process(user);
}
}
For a single condition, either works. Choose whichever your team prefers and apply consistently.
In switch Statements
break has a special role in traditional switch statements (covered in Topic 3.2). It exits the switch, preventing fallthrough to the next case:
switch (day) {
case "MONDAY":
schedule = "Heavy meetings";
break; // exit the switch — without this, fallthrough!
case "TUESDAY":
schedule = "Deep work";
break;
}
Modern switch expressions (case "MONDAY" -> ...) don't have fallthrough, so they don't need break. Use the modern form when available.
continue is not valid inside a switch (only inside loops). If you're inside a loop containing a switch, continue in the switch's case body affects the enclosing loop, not the switch:
for (String day : days) {
switch (day) {
case "MONDAY":
if (skipMondays) continue; // continues the outer loop
break; // exits the switch
case "FRIDAY":
process(day);
break;
}
}
continue Doesn't Skip the Update
A subtle point about continue in a for loop: it DOES run the update expression. Just not the rest of the body:
for (int i = 0; i < 5; i++) {
if (i == 2) continue;
System.out.println(i);
}
// Prints 0, 1, 3, 4
// continue skipped the println but i++ still ran
This is different from in a while loop, where continue jumps directly to the condition check. If the while body increments a counter, continue skips that increment:
int i = 0;
while (i < 5) {
if (i == 2) continue; // infinite loop! i never advances past 2
System.out.println(i);
i++;
}
This is a common bug: using continue in a while loop without remembering that the update happens inside the body. The fix:
int i = 0;
while (i < 5) {
if (i == 2) {
i++;
continue;
}
System.out.println(i);
i++;
}
Or, use a for loop instead — the update in the header runs even on continue.
Interactive animation: continue in a for still runs i++, but in a while it can skip the line that advances the loop.
Common Pitfalls
Warning:
continuein a while loop without advancing the counter. Causes infinite loops. The counter increment isn't in the loop header like infor. Be extra careful with continue in while loops.
breakonly exits the innermost loop. If you need to break out of an outer loop from inside a nested loop, you either need a flag, an extracted method with return, or a labeled break (Topic 3.8).Forgetting
breakin traditional switch. Causes silent fallthrough. Use modern switch expressions to avoid this footgun entirely.Overusing break and continue. Code with many break/continue statements can be hard to follow. Each is a "side door" out of the natural flow. For complex iteration logic, consider extracting a method.
Using break when return is cleaner. Inside a method,
returnexits not just the loop but the entire method. If the loop's purpose is to find a value and return it,returnis often cleaner thanbreakfollowed by a return after the loop:// Less clean User found = null; for (User user : users) { if (user.getId() == targetId) { found = user; break; } } return found; // Cleaner for (User user : users) { if (user.getId() == targetId) { return user; } } return null;
Why This Matters in Interviews
Interview: Break and continue questions are common in output-prediction puzzles. Knowing exactly when each fires, and whether
continuein a while loop advances the counter, separates careful candidates from careless ones. In coding interview problems, the right use of break (for early termination) and continue (for filtering) is often what makes a solution clean.Topic 3.9 has the full Interview Lens including output-prediction examples.
Try It
Exercise: Verify each behavior in your IDE:
- Write a loop that finds the first negative number in an
int[]and exits with break- Write a loop that prints only the odd numbers from 1 to 20 using continue
- Write a while loop with continue, but forget to advance the counter — observe the infinite loop (be ready to kill it!)
- Write nested loops where break exits only the inner loop. Verify the outer loop continues
- Refactor a search loop that uses break + return-after-loop into a single return inside the loop. Compare readability.
Each exercise tests a real behavior. The third one is particularly instructive — once you've experienced the infinite loop, you'll always remember to advance counters in while loops with continue.