🔀 Control Flow · Beginner

for loop in Java

Init; condition; update, and off-by-one errors.

🧩 The mystery"Print the numbers 1 to 10." Easy, right? Yet the loop that 'obviously' does this is one of the most common bugs ever written. Let's make sure it isn't yours.

Three parts, one line

A for header holds init (runs once), condition (checked before each pass) and update (runs after each pass). Order: init, check, body, update, check, body, update... The first pass sees i == 0; the update hasn't run yet.

for (int i = 0; i < 3; i++) {
    System.out.println(i);
} // 0, 1, 2
🔮 Predict it

Your turn

What prints?

for (int i = 10; i > 0; i -= 4) {
    System.out.print(i + " ");
}
  1. 10 6 2
  2. 10 6 2 -2
  3. 6 2
  4. 10 6
Show the answer

i takes 10, 6, 2. The next update makes it -2, and -2 > 0 is false, so the loop stops before printing it. Updates can step by any amount, up or down.

Counting the passes

for (int i = 0; i < n; i++) runs exactly n times (0 to n-1). Switch < to <= and it runs n + 1 times, because both ends are included. i = 0; i <= 10 visits 0, 1, ..., 10: that's 11 values, not 10.

for (int i = 0; i < 5; i++) { }  // 5x
for (int i = 0; i <= 5; i++) { } // 6x
for (int i = 1; i <= 5; i++) { } // 5x
🤔 Think first

Count without running

How many times does for (int i = 3; i <= 7; i++) run?

Think about it, then reveal the answer

5 times: i = 3, 4, 5, 6, 7. Quick formula for <=: last minus first, plus one (7 - 3 + 1). Forgetting that +1 is the classic off-by-one.

⚠️ The trap

Off by one

This is meant to print 1 to 10, but i < 10 stops when i is 10, so 10 never prints. The same bug in reverse: i > 1 in a countdown never prints 1. Check both ends: where does it start, and is the last value included?

// meant: 1 to 10
for (int i = 1; i < 10; i++) {
    System.out.println(i);
} // stops at 9! use i <= 10
🔮 Predict it

Hands off the counter

The body ALSO changes i. What prints?

int sum = 0;
for (int i = 0; i < 8; i++) {
    sum += i;
    i++;
}
System.out.println(sum);
  1. 28
  2. 12
  3. 16
  4. 6
Show the answer

i is incremented twice per pass: once in the body, once in the update. So the body sees i = 0, 2, 4, 6, and sum = 0 + 2 + 4 + 6 = 12. Changing the loop variable inside the body makes loops hard to read; avoid it.

Scope and optional parts

A variable declared in the header (int i) exists only inside the loop. And all three parts are optional: for (;;) has no condition, so it loops forever (until a break or return).

for (int i = 0; i < 3; i++) { }
// i doesn't exist here
for (;;) {
    // runs until break/return
}
💼 In the real world

In real projects

Off-by-one errors cause pagination that shows 11 items per page, reports that skip the last day of the month, and batch jobs that never process the final record. Code reviewers check loop boundaries first, and good unit tests always include the edge cases: empty input, one item, exactly the limit.

Key takeaways

  1. Order: init, check, body, update, check, body, update...
  2. for (int i = 0; i < n; i++) runs exactly n times
  3. A variable declared in the header only exists inside the loop
  4. All three parts are optional: for (;;) loops forever

💡 A for header is a recipe card: 'start here, keep going while this is true, take this step each time'.

🤯 Did you know?

Off-by-one errors are also called "fencepost errors": a 30 m fence with a post every 10 m needs 4 posts, not 3. Count the gaps and the posts separately!

Practice questions

What does this print?

for (int i = 1; i <= 10; i += 3) {
    System.out.print(i + " ");
}
  1. 1 4 7
  2. 1 4 7 10
  3. 4 7 10
  4. 1 4 7 10 13
Check your answer

1 4 7 10. i takes 1, 4, 7, 10. Then it becomes 13, 13 <= 10 is false, and the loop stops.

How many times does the body of for (int i = 0; i <= 10; i++) run?

  1. 10
  2. 11
  3. 9
  4. 12
Check your answer

11. It runs for i = 0, 1, ..., 10. That's 11 values because both ends are included.

Next: a loop with no counter at all, that can never go out of bounds. What's the catch?