The three-part for loop
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
Prints:
0
1
2
3
4
The three sections inside the parentheses, separated by semicolons, each do one job:
int i = 0— setup, runs once before anything elsei < 5— the test, checked before every pass; the body only runs if it's truei++— the step, runs after every pass
i++ is shorthand for i = i + 1.
The order of operations is worth saying out loud, because once you have it you can hand-trace any loop:
setup → test → body → step → test → body → step → ... → test fails → done
Notice what that order means: the test runs one extra time at the end, and fails. And the step runs after the body, not before — so on the first pass through the body, i is still 0.
That's why it prints 0 through 4 and stops. When i becomes 5, the test 5 < 5 is false, the body is skipped, and the loop exits. The variable ends up at 5, but 5 never made it into the body.
The wrong mental model: reading i < 5 as "run five times." Sometimes that's true, sometimes it isn't. What it literally means is "keep going while i is under 5." Trace the three steps instead of counting — it works every time, including the weird loops.
Walking an array: the enhanced for
int[] scores = {88, 92, 79};
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
Prints:
88
92
79
When you don't actually need the index, there's a cleaner form:
for (int score : scores) {
System.out.println(score);
}
Same output. Read it as "for each score in scores." Each time round, score holds the next element.
Prefer this one when you can. It's shorter, and more importantly it makes off-by-one errors impossible — there's no index to get wrong.
Two small things in that code that trip people up constantly:
- Array length is
scores.length— no parentheses, it's a field. - String length is
name.length()— with parentheses, it's a method.
There's no logic to the inconsistency. It's historical. You will mix them up; the compiler will catch it instantly.
while and do-while
int count = 0;
while (count < 3) {
System.out.println(count);
count++;
}
Prints 0, 1, 2 on separate lines. A while loop is a for loop with the setup and step moved out — which means you are now responsible for the step. Forget count++ and the condition never changes and the loop never ends.
Use for when you know the count up front. Use while when you don't — "keep going until the user types quit."
do {
System.out.println("runs at least once");
} while (false);
Prints runs at least once, even though the condition is false, because do-while runs the body first and tests afterward. Useful for "prompt the user, then check whether what they typed was valid."
break and continue
for (int i = 0; i < 10; i++) {
if (i == 5) break; // leave the loop entirely
if (i % 2 == 0) continue; // skip the rest of this pass
System.out.println(i);
}
Prints:
1
3
Trace it: at i=0, 0 % 2 == 0 is true, so continue jumps straight to the step and nothing prints. i=1 is odd, so it prints. i=2 skipped, i=3 prints, i=4 skipped, and at i=5 break ends the loop before anything else runs.
continue skips to the next pass. break abandons the loop completely.
Nested loops
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
System.out.printf("%d x %d = %-4d", j, i, i * j);
}
System.out.println();
}
Prints:
1 x 1 = 1
1 x 2 = 2 2 x 2 = 4
1 x 3 = 3 2 x 3 = 6 3 x 3 = 9
The key to nested loops is one sentence: the inner loop is part of the outer loop's body. So the outer loop takes one step, and the inner loop runs to completion. Then the outer takes another step, and the inner runs to completion again — starting over from the beginning each time.
Here j <= i makes the inner loop grow by one each round, which is what gives the triangle shape. %-4d means "a whole number, left-aligned, padded to 4 characters" — that's what keeps the columns lined up.
The bare System.out.println() with nothing in it prints just a line break. It sits in the outer loop's body, after the inner loop, so it ends each row.
The accumulator pattern
This shape solves a surprising number of problems.
int total = 0;
for (int i = 1; i <= 100; i++) {
total += i;
}
System.out.println(total);
Prints 5050. (total += i is shorthand for total = total + i.)
The pattern: make a container before the loop, update it inside the loop, read it after. Summing, counting, finding a maximum, building a string — all the same skeleton. The only thing that changes is what "update" means.
Notice that total is declared before the loop. If you declared it inside, it would be created fresh at zero on every pass and you'd end up with 100, not 5050.
Finding a maximum uses the same shape:
int[] nums = {3, 9, 1, 7};
int max = nums[0];
for (int n : nums) {
if (n > max) {
max = n;
}
}
System.out.println(max);
Prints 9. Start by assuming the first element is the winner, then replace it whenever you meet something bigger.
Why nums[0] and not 0? Because starting at 0 silently breaks on all-negative data:
int[] temps = {-3, -9, -1};
int max = 0; // wrong start
for (int n : temps) {
if (n > max) max = n;
}
System.out.println(max); // 0
It prints 0 — a temperature that isn't in the array and never was. Nothing crashes; the answer is just wrong. Seeding with nums[0] guarantees max always holds a real element from the array.
The starting value is the only genuinely tricky part of this pattern. Sums start at 0. Products start at 1. Maximums and minimums start at the first element.
Ten-second check
for (int i = 0; i < 3; i++);
System.out.println(i);
What's wrong here?
There's a stray semicolon right after the for. That semicolon is the entire loop body — an empty statement. So the loop spins three times doing nothing, and the println isn't in the loop at all. It then fails to compile anyway, because i was declared inside the for and doesn't exist outside it. A misplaced semicolon is nearly invisible to the eye, so when a loop "doesn't loop," check for one first.