Three kinds of “again”#
English has several ways to say “do it again”, and they are not the same:
- “Run four laps.” You know the number before you start. That is a
forloop. - “Wait here until the bus comes.” You check first, and if the bus is already there you do not wait at all. That is a
whileloop. - “Taste the soup, then decide whether it needs more salt.” You always taste at least once, and decide afterwards. That is a
do-whileloop.
Every loop has a condition, a boolean that decides whether to go round again, and a body, the code that repeats. One trip through the body is a pass (also called an iteration). The three loops differ only in when the condition is checked and where the counting lives.
while: check first#
A while loop checks its condition before every pass. If the condition is false the very first time, the body never runs at all.
public class Countdown {
public static void main(String[] args) {
int n = 3;
while (n > 0) {
System.out.print(n + " ");
n--;
}
System.out.println("liftoff");
int m = 0;
while (m > 0) {
System.out.println("never printed");
}
System.out.println("the second loop ran zero times");
}
}
3 2 1 liftoff
the second loop ran zero times
The first loop counts 3, 2, 1. Notice that something inside the body has to change the condition, here n--. If nothing ever does, the condition stays true and the loop never ends. That is an infinite loop.
The second loop's condition is false from the start, so its body runs zero times. That is the defining feature of while.
for: counting, all in one line#
A for loop is a while loop with the counting gathered into its header:
for (int lap = 1; lap <= 4; lap++)
\_________/ \______/ \___/
start once check change after
each time each pass
public class Laps {
public static void main(String[] args) {
for (int lap = 1; lap <= 4; lap++) {
System.out.print("lap " + lap + " ");
}
System.out.println();
int lap = 1;
while (lap <= 4) {
System.out.print("lap " + lap + " ");
lap++;
}
System.out.println();
for (int i = 10; i > 0; i -= 3) {
System.out.print(i + " ");
}
System.out.println();
}
}
lap 1 lap 2 lap 3 lap 4
lap 1 lap 2 lap 3 lap 4
10 7 4 1
The first two loops do exactly the same thing. The for version keeps the start, the check and the change together, where you cannot forget one. That is why for is the natural choice whenever you know how many passes you need.
The update part does not have to be ++. The last loop counts down by 3: 10, 7, 4, 1. The next value, -2, fails i > 0.
do-while: at least once#
A do-while puts the check at the bottom, so the body always runs at least once before any question is asked:
public class AtLeastOnce {
public static void main(String[] args) {
int tries = 50;
do {
System.out.println("do-while body ran, tries = " + tries);
tries++;
} while (tries < 10);
int other = 50;
while (other < 10) {
System.out.println("while body ran");
other++;
}
System.out.println("finished");
}
}
do-while body ran, tries = 50
finished
Both loops have the same condition, and it is false from the start. The do-while ran once anyway; the while never ran. Note the semicolon after while (tries < 10);. A do-while needs it, and it is easy to forget.
The classic use is a menu or a prompt: you cannot check the user's answer until you have asked the question, so asking has to happen first.
Choosing a loop#
| If… | Use |
|---|---|
| you know the number of passes, or you are counting through a range | for |
| you repeat until something happens, and it may already have happened | while |
| the body must run at least once before you can decide | do-while |
Any of the three can do any job. The right choice is the one that makes the reader's life easiest, which usually means the one where the counter or the condition is hardest to get wrong.
Tracing a nested loop#
A nested loop is a loop inside another loop. Think of a clock: the minute hand goes all the way round once for every single step of the hour hand. The inner loop finishes all its passes for each pass of the outer loop.
public class Grid {
public static void main(String[] args) {
int count = 0;
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 4; col++) {
count++;
}
}
System.out.println("inner body ran " + count + " times");
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= row; col++) {
System.out.print(row * col + " ");
}
System.out.println();
}
}
}
inner body ran 12 times
1
2 4
3 6 9
The first part only counts. The outer loop makes 3 passes and the inner loop makes 4 passes for each of them, so the inner body runs 3 × 4 = 12 times. For a question that asks “how many times does this run?”, multiply.
The second part is the kind exams like, because the inner loop's limit depends on the outer loop's counter. Trace it with a table, one row per outer pass:
row | col goes | prints |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1, 2 | 2 4 |
| 3 | 1, 2, 3 | 3 6 9 |
Each row prints row * col for every col up to row, and println() after the inner loop starts a new line. Filling in a table like this, one row per outer pass, is the fastest reliable way to answer a tracing question without a computer.
break and continue#
Two keywords change a loop's flow from the inside.
| Keyword | Does |
|---|---|
break | leaves the loop immediately; execution carries on after it |
continue | skips the rest of this pass and goes on to the next one |
public class Skip {
public static void main(String[] args) {
for (int i = 1; i <= 8; i++) {
if (i == 6) {
break;
}
if (i % 2 == 0) {
continue;
}
System.out.print(i + " ");
}
System.out.println("| after the loop");
}
}
1 3 5 | after the loop
The even numbers 2 and 4 hit continue, so they skip the print. At 6, break ends the loop, so 7 is never reached even though it is odd.
In a for loop, continue still runs the update part, i++, before the next check. In a while loop, the update is just an ordinary line in the body, and continue can jump over it. Then the counter never changes, and the loop never ends. If you need continue, a for loop is usually the safer home for it.
Looping over a String#
A String's characters are numbered from 0 to length() - 1, so a for loop can visit each one with charAt:
public class Letters {
public static void main(String[] args) {
String word = "Kestrel";
int vowels = 0;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
vowels++;
}
}
System.out.println(word + " has " + vowels + " vowels");
String backwards = "";
for (int i = word.length() - 1; i >= 0; i--) {
backwards = backwards + word.charAt(i);
}
System.out.println(backwards);
}
}
Kestrel has 2 vowels
lertseK
The first loop counts the lowercase vowels. The second walks backwards, starting at the last index, word.length() - 1, and building a new String one character at a time. The condition i >= 0 makes sure index 0 is included.
Off by one, and other one-character bugs#
The commonest loop bug is running once too many or once too few. Check both ends of every loop.
| Header | Values of i |
|---|---|
for (int i = 1; i <= 10; i++) | 1 to 10: ten passes |
for (int i = 1; i < 10; i++) | 1 to 9: nine passes |
for (int i = 0; i < 10; i++) | 0 to 9: ten passes |
for (int i = 0; i <= s.length(); i++) | one past the end: charAt will crash |
Two more one-character mistakes. A semicolon straight after the header becomes the loop's whole body:
public class Stray {
public static void main(String[] args) {
for (int i = 0; i < 3; i++); {
System.out.println("printed once, not three times");
}
}
}
printed once, not three times
The loop runs three times doing nothing, and the block in braces runs once, on its own. And a variable declared in a for header only exists inside that loop:
public class Scope {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
}
System.out.println(i);
}
}
Scope.java:5: error: cannot find symbol
System.out.println(i);
^
symbol: variable i
location: class Scope
1 error
Declare the counter before the loop if you need its value afterwards.
while checks first, do-while checks last, and for is a while with its counting in the header. For a nested loop, the inner loop runs all the way through for every single pass of the outer one.
Where these go wrong#
- Nothing in the body changes the condition. The loop never ends.
<where you meant<=, or the other way round. Check the first and last value by hand.- Using
i <= s.length()withcharAt(i). The last index islength() - 1. - A semicolon after the
forheader. The loop body is empty. - Forgetting the semicolon after
do { } while (...). That one is required. - Using the
forcounter after the loop. It no longer exists. continuein awhileloop that skips the update. Infinite loop.- Adding instead of multiplying for a nested loop. 3 outer passes of a 4-pass inner loop is 12, not 7.
Test yourself in the free Kestrel Exams app
Topic-selectable practice — offline, no ads, no account.
Practice loops →Frequently asked questions#
What is the difference between for, while and do-while in Java?
A while loop checks its condition before each pass, so it can run zero times. A do-while loop checks after each pass, so it always runs at least once. A for loop is a while loop with its start, check and update written together in one header, which makes it the natural choice when you know how many passes you need.
When should I use a do-while loop?
When the body has to run at least once before you can decide whether to repeat it. The standard example is showing a menu or asking for input: you cannot check the answer until you have asked the question.
How do I count how many times a nested loop runs?
Multiply. If the outer loop makes 3 passes and the inner loop makes 4 passes each time, the inner body runs 3 times 4, which is 12. If the inner loop's limit depends on the outer counter, trace it with a table, one row per outer pass.
What is the difference between break and continue?
break leaves the loop completely and carries on after it. continue skips only the rest of the current pass and goes on to the next one. In a for loop the update, such as i++, still runs after continue.
Why is my for loop only running once?
Check for a semicolon straight after the header, as in for (int i = 0; i < 3; i++); { ... }. The semicolon is an empty statement and becomes the loop's whole body, so the loop runs three times doing nothing and the block after it runs once.
Why can't I use my for loop variable after the loop?
A variable declared in the for header belongs to the loop, so its scope ends when the loop does. javac reports cannot find symbol. Declare the variable before the loop if you need its value afterwards.
Something here not clear? A topic you wish we covered? Tell us. We read every message, and a request is the fastest way to get a guide written — several of these exist because somebody asked.
