skip to content
everything.basics.controlFlow

control flow

if, loops, break, continue, and labeled breaks (yes, java has those).

chapter
chapter 02.06
reading time
6 min read

code runs top to bottom until something says otherwise. if picks a branch, loops (while, do, for, for-each) repeat, and break / continue jump out of or ahead in a loop. java also has labeled breaks, for jumping out of nested loops in one go.

the picture

think of a program as a recipe. by default you do step 1, then step 2, then step 3. control flow is the "if the dough is sticky, add flour" and the "stir until smooth" bits: the steps that decide which step comes next.

every one of these statements is built on a condition: an expression that's a boolean. not a number, not "something truthy". exactly true or false.

java
int x = 5;
if (x = 5) IO.println("five");

error: incompatible types: int cannot be converted to boolean. in C that line compiles and is a classic bug (it assigns). java refuses, because if only accepts booleans. you meant x == 5.

if, else if, else

Main.java
void main() {
    int score = 72;
    if (score >= 90) {
        IO.println("A");
    } else if (score >= 70) {
        IO.println("B");
    } else {
        IO.println("C");
    }
}
output
B

conditions are checked top to bottom, and the first true one wins. everything after it is skipped, even if it would also be true. so order matters: put score >= 70 first and nobody ever gets an A.

braces are optional for a single statement. use them anyway: the day someone adds a second line under an unbraced if, it silently runs every time.

the for loop, in slow motion

a for loop packs four things into one line, and they do not run left to right:

int sum = 0;
for (int i = 0; i < 3; i++) {
sum += i;
}
IO.println(sum); // 3
i0
sum0
what ran, in order
init

init runs exactly once, before anything else. i is born here, and it only exists inside the loop.

step 1/12
step through. the amber part is running right now; the check turns green when it lets the body run and red when it ends the loop. notice the update runs after the body, and the check runs one more time than the body.

for (init; check; update): init once, then check → body → update, over and over, until the check is false. use it when you're counting. the variable declared in init (i) only exists inside the loop.

while and do-while

while is the loop for "until something happens", when you can't know the count up front:

Main.java next to run
  1. 1void main() {
  2. 2 int n = 6;
  3. 3 int steps = 0;
  4. 4 while (n != 1) {
  5. 5 if (n % 2 == 0) {
  6. 6 n = n / 2;
  7. 7 } else {
  8. 8 n = 3 * n + 1;
  9. 9 }
  10. 10 steps++;
  11. 11 }
  12. 12 IO.println(n + " after " + steps + " steps");
  13. 13}
stdout
·
stack
main
no locals yet
heap
nothing allocated yet.

main starts.

step 1/37
a real recording of this loop running (the collatz sequence from 6). step through, or press play. watch the check on line 4 run before every round, including the last one that fails.

do { ... } while (check); is the same loop with the check moved to the bottom, so the body always runs at least once. it's rare, mostly for "ask until the input is valid".

Main.java
void main() {
    int n = 10;
    while (n < 5) { IO.println("while " + n); n++; }
    do { IO.println("do " + n); n++; } while (n < 5);
}
output
do 10

the while checks first, sees 10 < 5 is false, and never runs. the do runs once, then checks.

for going through every element of an array or a collection, there's the for-each loop: for (int x : xs) { ... }. no index, no off-by-one. it's the one you'll use most once you meet arrays.

break, continue, and labels

break leaves the loop it's in. continue skips the rest of the current round and goes to the next one (in a for, that means the update runs next). in nested loops, both only affect the innermost loop, unless you give a loop a label and name it.

when i == 1 && j == 2 =
outer:
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i == 1 && j == 2) break;
IO.print(i + "" + j + " ");
}
IO.print("| ");
}
IO.println("done");
i ↓ · j →|
00010203|
1011break13|
20212223|
30313233|
■ ran□ skipped□ the jump
stdout 00 01 02 03 | 10 11 | 20 21 22 23 | 30 31 32 33 | done

break leaves the innermost loop only. row 1 stops early, but its "|" still prints and the outer loop carries on with row 2.

same nested loops, one jump at i == 1 && j == 2. pick the jump and compare the grid and the real output. the label outer: names the outer loop so a jump from the inner loop can target it.

labeled breaks are rare, and that's fine. when you need to stop searching a 2d grid the moment you find something, break outer is clearer than a found flag checked in two places. (pulling the loops into a method and using return is often clearer still.)

predict the output
java
for (int i = 0; i < 3; i++); {
    IO.println("hi");
}
keylines
  • conditions must be real booleans. if (x = 5) doesn't compile in java.
  • if / else if / else: the first true branch wins, the rest are skipped.
  • for runs init once, then check → body → update until the check is false.
  • while checks first, do-while runs once then checks.
  • break and continue affect the innermost loop. label a loop to target it from inside.