Java For Loop

The for loop is Java’s primary tool for repeating a block of code a known or countable number of times. It packs the loop’s setup, its stopping condition, and its per-iteration update into a single, compact header, which makes it the natural choice whenever you’re counting, scanning an array, or processing a fixed range of values. Nearly every Java program you write will use a for loop somewhere — for printing reports, validating input, building collections, or iterating over data structures. Understanding exactly how it evaluates its three parts, and in what order, is the key to avoiding the subtle bugs that trip up almost every beginner.

Overview: How the For Loop Works

A for loop repeats a statement or block of statements while a boolean condition remains true. What makes it different from a plain while loop is that it bundles three separate steps — initialization, condition check, and update — directly into the loop header, so all the loop’s bookkeeping lives in one place instead of being scattered across your code.

Internally, the Java compiler treats for as syntactic sugar for a while loop. When you write:

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

the compiler generates bytecode that behaves exactly like this equivalent while loop:

int i = 0;
while (i < 5) {
    System.out.println(i);
    i++;
}

This equivalence explains the execution order that trips up so many beginners:

  1. The initialization runs exactly once, before the loop starts.
  2. The condition is checked before every iteration, including the very first one. If it is false immediately, the loop body never executes at all.
  3. The loop body runs.
  4. The update expression runs after the body finishes.
  5. Control returns to step 2, and the cycle repeats until the condition becomes false.

Because the loop variable (i above) is declared inside the for header, its scope is limited to the loop itself — it does not exist before the loop and cannot be referenced after it ends. If you need the counter’s final value after the loop, declare it outside the header instead.

At the bytecode level, the JVM has no special “for loop” instruction. Both for and while compile down to the same pattern: a conditional branch instruction (such as if_icmpge) that jumps past the body when the condition fails, and an unconditional goto that sends control back to the condition check. The for loop is purely a source-level convenience — its runtime performance is identical to a hand-written while loop.

Syntax

for (initialization; condition; update) {
    // loop body
}
Part Purpose Notes
initialization Runs once, before the loop begins Typically declares and initializes a counter, e.g. int i = 0. You can initialize multiple variables of the same type here, separated by commas.
condition A boolean expression checked before every iteration The loop continues as long as this evaluates to true. If omitted, it defaults to true, producing an infinite loop unless you break out of it.
update Runs after each iteration’s body completes Usually increments or decrements the counter, e.g. i++. Can contain multiple comma-separated expressions.
{ } The loop body One or more statements to repeat. Braces are optional for a single statement but strongly recommended for clarity.

All three header parts are optional — for (;;) { } is a valid, intentionally infinite loop — but omitting them is rare and usually makes the loop’s intent harder to read.

Examples

Example 1: Counting with a Basic For Loop

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Count: " + i);
        }
    }
}

Output:

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5

The loop starts with i at 1, checks i <= 5 before every pass, prints the current count, then increments i. Once i becomes 6, the condition fails and the loop ends — giving exactly five iterations.

Example 2: Summing and Averaging an Array

public class Main {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 90, 88};
        int sum = 0;
        for (int i = 0; i < scores.length; i++) {
            sum += scores[i];
        }
        double average = (double) sum / scores.length;
        System.out.println("Sum: " + sum);
        System.out.println("Average: " + average);
    }
}

Output:

Sum: 433
Average: 86.6

Here the loop uses scores.length instead of a hard-coded number, so it works correctly no matter how many elements the array has. Note the < (not <=) against .length — array indices run from 0 to length - 1, so this is the safe, idiomatic bound. The cast (double) forces floating-point division so the average isn’t truncated to an integer.

Example 3: Nested For Loops for a Multiplication Table

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                System.out.print(i * j + "\t");
            }
            System.out.println();
        }
    }
}

Output:

1	2	3
2	4	6
3	6	9

A loop nested inside another loop runs the inner loop to completion for every single iteration of the outer loop. Here the outer loop picks a row (i), and for each row the inner loop walks through every column (j), printing i * j. With two 3-iteration loops, the body runs 3 times 3, or 9 times in total — nested loops multiply their iteration counts, so a triple-nested loop over three collections of size n costs roughly n * n * n operations.

Under the Hood: Step by Step

Let’s trace exactly what happens when the JVM executes for (int i = 1; i <= 5; i++) { System.out.println("Count: " + i); } from Example 1:

  1. i is allocated as a local variable slot and initialized to 1.
  2. The JVM evaluates i <= 5. Since 1 <= 5 is true, execution falls through into the loop body.
  3. The body runs, printing Count: 1.
  4. The update expression i++ runs, changing i to 2.
  5. The JVM jumps back to the condition check (a goto instruction in the compiled bytecode). Since 2 <= 5 is true, the body runs again.
  6. This repeats until i becomes 6. At that point 6 <= 5 is false, so the JVM jumps past the loop body entirely, and execution continues with whatever statement follows the loop.

Five iterations happen in total (i equal to 1, 2, 3, 4, and 5), because the condition is checked before what would be the sixth iteration. This “check-before-run” behavior is why a for loop is called a pre-test loop: if the condition is already false the very first time it’s checked, the body — and the update — never run at all.

Common Mistakes

Mistake 1: An Accidental Semicolon After the Loop Header

A stray semicolon right after the closing parenthesis turns the loop body into an empty statement, so the code that looks like it belongs to the loop actually only runs once, after the loop finishes.

public class Main {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 0; i < 5; i++);
        {
            count++;
        }
        System.out.println("Count: " + count);
    }
}

Output:

Count: 1

The semicolon right after i++) is itself the entire loop body — an empty statement — so the loop silently runs five times doing nothing. The { count++; } block is not part of the loop at all; it is just an ordinary block that executes once, afterward. The fix is to remove the semicolon so the braces become the loop’s actual body:

public class Main {
    public static void main(String[] args) {
        int count = 0;
        for (int i = 0; i < 5; i++) {
            count++;
        }
        System.out.println("Count: " + count);
    }
}

Output:

Count: 5

Mistake 2: Modifying the Loop Variable Inside the Body

Changing the loop counter yourself, in addition to the automatic update expression, is a common way to silently skip iterations.

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            i++;
        }
    }
}

Output:

0
2
4

Every pass through the body increments i once manually and then again in the loop’s own update expression, so the counter advances by two per iteration instead of one, and half the expected values are skipped. Removing the manual increment fixes it:

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
    }
}

Output:

0
1
2
3
4

Mistake 3: Off-by-One Errors with Array Bounds

Using <= instead of < when comparing against an array’s .length is one of the most common Java bugs. Valid indices for an array run from 0 to length - 1, so a loop written as for (int i = 0; i <= arr.length; i++) reads one index past the end of the array on its final pass, throwing an ArrayIndexOutOfBoundsException at runtime. The fix is simply to use < against .length, as shown in Example 2 — or, if you don’t actually need the index, switch to a for-each loop (for (int score : scores)) so there is no index to get wrong.

Best Practices

  • Declare the loop variable inside the header (for (int i = ...)) whenever you don’t need it after the loop — this limits its scope and prevents accidental reuse elsewhere in the method.
  • Prefer < over <= when looping over array or list indices, paired with .length or .size(), to avoid off-by-one errors.
  • Use a for-each loop (for (int score : scores)) instead of an index-based for loop whenever you only need the values, not the index — it’s shorter and eliminates an entire class of bugs.
  • Never modify the loop counter inside the body unless you have a very deliberate reason, and comment why — it makes the number of iterations hard to reason about otherwise.
  • Keep the loop body focused; if it grows past a handful of lines, extract it into a well-named method.
  • For an intentionally infinite loop (such as a server’s main event loop), write while (true) rather than for (;;) — most readers scan while (true) faster and recognize the intent immediately.
  • Watch for integer overflow in long-running loops — an int counter that increments past Integer.MAX_VALUE wraps around to a large negative number instead of throwing an error.

Practice Exercises

  1. Write a program with a for loop that prints all even numbers from 2 to 20, inclusive.
  2. Given the array int[] temps = {72, 68, 75, 80, 65};, write a for loop that finds and prints the highest temperature.
  3. Use a nested for loop to print a right triangle of asterisks with 5 rows, where row n contains n asterisks (row 1 prints one *, row 5 prints five).

Summary

  • A for loop bundles initialization, condition, and update into one header, and is ideal when the number of iterations is known or countable in advance.
  • The condition is checked before every iteration; if it starts out false, the body never runs at all.
  • The loop variable declared in the header is scoped to the loop and no longer exists once it ends.
  • The JVM compiles a for loop into the same bytecode as an equivalent while loop, so there is no performance difference between them.
  • Nested for loops let you process grids, tables, and multi-dimensional data, but their iteration count multiplies with each added level of nesting.
  • Common bugs include a stray semicolon right after the header, accidentally modifying the loop counter inside the body, and off-by-one index errors against .length.
  • Prefer for-each loops when you don’t need the index, and keep loop bodies short and focused.