Skip to content
Java control flow 6 min read

for Loop

When you need to repeat a block of code a specific number of times — or iterate over a range of values — the for loop is your go-to tool. It packs the initialization, condition check, and update all into one line, making your intent crystal clear at a glance.

Basic Syntax

for (initialization; condition; update) {
    // body — runs as long as condition is true
}
  • initialization — runs once before the loop starts (e.g., int i = 0)
  • condition — checked before every iteration; loop stops when it becomes false
  • update — runs after every iteration (e.g., i++)

A simple countdown example:

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 variable i is scoped to the loop itself — you cannot use it after the closing brace.

How Execution Flows

It helps to picture the order of operations:

  1. initialization executes (int i = 1)
  2. condition is evaluated (i <= 5) — if false, skip the body entirely
  3. body runs
  4. update executes (i++)
  5. Go back to step 2

Tip: Because the condition is checked before the body, a for loop is a pre-condition loop — the body can run zero times if the condition starts out false.

Iterating Over an Array

The classic use case is walking through an array by index:

int[] scores = {88, 92, 75, 61, 99};

for (int i = 0; i < scores.length; i++) {
    System.out.println("Score[" + i + "] = " + scores[i]);
}

Output:

Score[0] = 88
Score[1] = 92
Score[2] = 75
Score[3] = 61
Score[4] = 99

Note: Always use i < scores.length (strictly less than), not i <= scores.length. Using <= causes an ArrayIndexOutOfBoundsException on the last iteration.

When you only need the values — not the index — prefer the for-each loop instead; it is shorter and less error-prone.

Counting Down

You can initialize i to the highest value and decrement:

for (int i = 5; i >= 1; i--) {
    System.out.println(i);
}
System.out.println("Liftoff!");

Output:

5
4
3
2
1
Liftoff!

Stepping by More Than One

The update expression can be any valid statement — not just i++:

// Print even numbers from 2 to 10
for (int i = 2; i <= 10; i += 2) {
    System.out.print(i + " ");
}

Output:

2 4 6 8 10 

Nested for Loops

You can place a for loop inside another. Each iteration of the outer loop triggers a complete run of the inner loop — classic for building grids or multiplication tables:

for (int row = 1; row <= 3; row++) {
    for (int col = 1; col <= 3; col++) {
        System.out.printf("%4d", row * col);
    }
    System.out.println();
}

Output:

   1   2   3
   2   4   6
   3   6   9

Warning: Deeply nested loops (3+ levels) multiply quickly. A triple loop over n elements is O(n³) — watch out for performance issues with large data sets.

Declaring Multiple Variables

You can declare and initialize multiple variables of the same type in the initialization section, separated by commas:

for (int i = 0, j = 10; i < j; i++, j--) {
    System.out.println("i=" + i + ", j=" + j);
}

Output:

i=0, j=10
i=1, j=9
i=2, j=8
i=3, j=7
i=4, j=6

Note: Both variables must share the same type (int here). You cannot mix int i = 0, long j = 10 in a single init clause.

Omitting Parts of the Header

All three sections of the for header are optional. You can omit any or all of them (but keep the semicolons):

int i = 0;
for (; i < 3; ) {   // init and update moved outside
    System.out.println(i);
    i++;
}

Omitting all three creates an infinite loop — useful when you control exit with break:

for (;;) {
    // runs forever unless a break or return is hit
}

Tip: An infinite loop with break is occasionally useful (e.g., event loops), but make sure there is always a reachable exit path, or your program will hang.

Using break and continue

  • break exits the loop immediately.
  • continue skips the rest of the current iteration and jumps straight to the update step.
for (int i = 0; i < 10; i++) {
    if (i == 7) break;       // stop at 7
    if (i % 2 == 0) continue; // skip even numbers
    System.out.print(i + " ");
}

Output:

1 3 5 

Labeled Loops

When you have nested loops and want break or continue to target the outer loop, use a label:

outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (j == 1) continue outer; // skip rest of inner, go to next outer iteration
        System.out.println("i=" + i + ", j=" + j);
    }
}

Output:

i=0, j=0
i=1, j=0
i=2, j=0

Labels are rarely needed, but they are cleaner than setting a boolean flag when you need to jump out of multiple nesting levels.

Under the Hood

Bytecode Translation

The Java compiler translates a for loop into a simple conditional branch in bytecode. This pseudocode of the bytecode roughly looks like:

INIT
LABEL_start:
  evaluate CONDITION → boolean
  if false → GOTO LABEL_end
  BODY
  UPDATE
  GOTO LABEL_start
LABEL_end:

There is no special “for loop” bytecode instruction — it compiles down to the same goto-based pattern as a while loop. The difference is purely syntactic sugar at the Java source level.

JIT Optimizations

The JVM’s Just-in-Time compiler (JIT) applies several aggressive optimizations once a loop is detected as “hot” (executed many times):

  • Loop unrolling — repeats the body 2–8 times per iteration to reduce branch overhead.
  • Auto-vectorization — rewrites array-walking loops into SIMD CPU instructions that process multiple elements in parallel.
  • Range-check elimination — for loops like for (int i = 0; i < arr.length; i++), the JIT proves array access arr[i] is always in bounds and removes the per-access check.
  • Induction variable optimization — hoists loop-invariant computations (e.g., arr.length) out of the loop so they are not re-evaluated on every iteration.

This is why a hand-written for loop in Java can approach native C performance for number-crunching tasks — the JIT does a lot of the heavy lifting automatically.

Stack Frame Behavior

Each loop iteration does not create a new stack frame. The loop variable lives in the existing method’s local variable table (a slot in the stack frame), and the update simply overwrites that slot on each pass. This makes tight loops very memory-efficient.

Common Mistakes

MistakeProblemFix
i <= arr.lengthArrayIndexOutOfBoundsException on last accessUse i < arr.length
i = i + 1 inside body and i++ in updateCounter increments twice per iterationPick one place to update
Modifying the collection while iteratingConcurrentModificationException or skipped elementsUse an iterator or copy the collection
Floating-point loop counter (for (double x = 0.0; x != 1.0; x += 0.1))Accumulating rounding error means x may never equal 1.0 exactlyUse an integer counter and compute the float value inside the body
Last updated June 13, 2026
Was this helpful?