The while loop and the infinite loop that lurks beside it, the increment and decrement operators, the for loop that puts all the control in one line, nested loops, and characters — plus the rule for choosing between the two kinds of loop. Follows Think Java 2e, Chapter 6 (Loops and Strings), Sections 6.1-6.6, pp. 89-98, cross-referenced against The Java Tutorials — The for Statement.
Subject: Java · 65 slides · code lesson
Open the interactive version of this deck
Title
Think Java 2e · Chapter 6 · Loops and Strings
Sections 6.1-6.6 · pp. 89-98
Objectives
This lesson follows Think Java 2e, Chapter 6 (Loops and Strings), Sections 6.1-6.6, pp. 89-98. Everything on these slides can be checked against those pages.
1. Write a while loop and trace it, and say what makes a loop terminate.
2. Recognise an infinite loop and name the missing piece that causes it.
3. Use ++, --, += and -= correctly.
4. Write a for loop, naming its initializer, condition and update.
5. Explain the scope of a variable declared in a for loop's initializer.
6. Trace a nested loop and say how many times the inner body runs.
7. Choose between for and while by asking whether the number of repetitions is known in advance.
Warm-up
The list from Lesson 1a is almost complete.
Discussion prompt
Name the five basic instructions that appear in every programming language. Which one have you still not met — and what would a program need it for?
Hint: Input, output, math, decision, and one more.
Answer:
Repetition is the last one. You have written input, output, math and — since Chapter 5 — decision.
Computers are often used to automate repetitive tasks, such as searching for text in documents. Repeating tasks without making errors is something computers do well and people do poorly, which is why this instruction exists at all.
Concept
A loop runs the same statements over and over until a condition becomes false. That is the whole idea, and everything in this lesson is about writing it clearly and making sure it stops.
Figure (svg): A flowchart for a while loop, showing the condition tested, the body executed, and an arrow looping back to the test
Downey & Mayfield, Think Java, 2nd edition (Green Tea Press / O'Reilly, 2020) — Think Java 2e, Chapter 6 (Loops and Strings), Sections 6.1-6.6, pp. 89-98 — Chapter 6 opens on printed page 89.
Section
Section 6.1
Concept
Using a while statement we can repeat the same code multiple times. Read it in English: start with n set to 3. While n is greater than 0, print the value of n and reduce it by 1. When you get to 0, print Blastoff!
int n = 3;
while (n > 0) {
System.out.println(n);
n = n - 1;
}
System.out.println("Blastoff!");| n at the test | condition | body runs? | output |
|---|---|---|---|
| 3 | 3 > 0 true | yes | 3 |
| 2 | 2 > 0 true | yes | 2 |
| 1 | 1 > 0 true | yes | 1 |
| 0 | 0 > 0 false | no — the loop ends | Blastoff! |
loop — A statement that executes a sequence of statements repeatedly.
loop body — The statements inside the loop.
Note the last row. The condition is checked before each pass, so when n reaches 0 the body does not run again — and the loop leaves n at 0 rather than -1.
Notation
The flow of execution is short enough to memorise, and worth memorising because every loop you write follows it.
Annotate
Compare this against the if statement from Chapter 5. The only difference is step 3 — an if runs its block at most once, and a while goes back to the test.
Worked example
A loop trace is a table with one row per pass. Building one is how you check a loop without running it.
int n = 3;
while (n > 0) {
System.out.println(n);
n = n - 1;
}
System.out.println("Blastoff!");| pass | n at the top | printed | n after the body |
|---|---|---|---|
| 1 | 3 | 3 | 2 |
| 2 | 2 | 2 | 1 |
| 3 | 1 | 1 | 0 |
| 4 | 0 | (condition false — no body) | 0 |
Write the variable's starting value.
Why: n is 3, set before the loop begins.
For each pass, test the condition first.
Why: The test uses the value in the column at the top of the row.
If it holds, run the body and record the new value.
Why: Printing does not change n; the assignment does.
Stop when the condition fails.
Why: The fourth row has no body — the loop exits and execution continues after the closing brace.
Verify: Expect 3, 2, 1, Blastoff! — four lines.
Why: Then check the count: the body ran three times and printed three numbers. If you predicted four numbers, you probably tested the condition after the body rather than before it.
Prediction
Count the passes, not the values.
int n = 5;
while (n > 2) {
System.out.println(n);
n = n - 1;
}| n at the test | runs? | printed |
|---|---|---|
| 5 | yes | 5 |
| 4 | yes | 4 |
| 3 | yes | 3 |
| 2 | no | — |
Predict first
How many numbers are printed?
Correct: 3
Why: The body runs while n is strictly greater than 2, so it runs for 5, 4 and 3 and stops when n reaches 2. Counting passes rather than reading the numbers is the reliable method — the values printed and the number of passes are different questions.
Concept
The body should change one or more variables so that eventually the condition becomes false. Otherwise the loop repeats forever — an infinite loop.
int n = 3;
while (n > 0) {
System.out.println(n);
// n never changes
}| pass | n | condition | will it ever end? |
|---|---|---|---|
| 1 | 3 | true | no |
| 2 | 3 | true | no |
| ... | 3 | true | no |
infinite loop — A loop whose condition is always true.
This prints 3 forever, or at least until you terminate the program. An endless source of amusement for computer scientists is the observation that the directions on shampoo — lather, rinse, repeat — are an infinite loop.
Trap
This looks like an infinite loop and may not be. n sometimes increases and sometimes decreases.
int n = 3;
while (n != 1) {
System.out.println(n);
if (n % 2 == 0) { // n is even
n = n / 2;
} else { // n is odd
n = 3 * n + 1;
}
}| n | even or odd | next n |
|---|---|---|
| 3 | odd | 3 * 3 + 1 = 10 |
| 10 | even | 10 / 2 = 5 |
| 5 | odd | 16 |
| 16, 8, 4, 2 | even each time | halved to 1 — the loop ends |
Starting at 3 the sequence is 3, 10, 5, 16, 8, 4, 2 and then stops. The code will not print 1, because the loop terminates as soon as n equals 1.
The honest position: for some starting values you can prove it terminates; in general nobody knows.
// powers of two obviously terminate:
// 16 -> 8 -> 4 -> 2 -> 1
// but does EVERY starting value reach 1?
// No one has proved it or disproved it.| question | answer |
|---|---|
| does it terminate for n = 16? | yes, provably — repeated halving |
| does it terminate for n = 27? | yes, after 111 steps — checked by running it |
| does it terminate for every n? | unknown — this is the Collatz conjecture |
The lesson is not about number theory. It is that you cannot always tell by looking whether a loop terminates — so a loop whose variable does not move steadily toward its exit condition deserves real scrutiny.
Error analysis
Three loops, each infinite for a different reason.
Annotate
i < 10 becomes more true rather than less. The update exists but has the wrong sign.k < 10 rather than k != 10. Testing for an exact value is fragile whenever the step size is not 1 — a comparison is almost always safer.If a program appears to hang, an infinite loop is the first thing to suspect — and these three questions will usually find it.
Socratic
It could have been tested afterwards.
Discussion prompt
A while loop checks its condition before running the body, so a loop whose condition starts false runs zero times. When would that be exactly the behaviour you want — and what would break if the check came after the first pass instead?
Hint: Think about processing a list that might be empty.
Answer:
Running zero times is essential whenever the work might not be needed: reading input until there is none left, when there was none to begin with; processing every character of a string, when the string is empty.
If the check came after the body, every such loop would do one pass on nothing — reading an item that is not there, or indexing past the end of an empty string. Testing first is what makes a loop safe on an empty case, and empty cases are extremely common.
Java does have a do-while loop that tests afterwards and therefore always runs at least once. It is used far less often, for exactly this reason.
Fill the middle
Print 10 down to 1.
Fill in the blanks
int n = 10;
while (n > 0) -} 1;
}
Why: n > 0 keeps the body running while n is 10 down to 1 and stops at 0, and subtracting 1 each pass is what moves n toward that exit. Using >= would print 0 as well, and forgetting the subtraction would give an infinite loop.
Section
Section 6.2
Concept
Assignments like i = i + 1 do not often appear in loops, because Java provides a more concise way. ++ is the increment operator and -- is the decrement operator.
int i = 1;
while (i <= 5) {
System.out.println(i);
i++; // add 1 to i
}| written | same as | meaning |
|---|---|---|
| i++ | i = i + 1 | increment |
| i-- | i = i - 1 | decrement |
| i += 2 | i = i + 2 | add 2 |
| i -= 2 | i = i - 2 | subtract 2 |
increment — Increase the value of a variable.
decrement — Decrease the value of a variable.
If you want to change a variable by an amount other than 1, use += and -=. These are not new operations — every one of them is an assignment written more briefly.
Picture it
A loop that steps by 2 rather than 1, using the compound assignment operator.
Figure (svg): A trace strip showing i taking the values 2, 4, 6 and 8 and then failing the condition at 10
The output is 2, 4, 6, 8, Who do we appreciate? — four passes, and then the condition fails at 10. Note that i finishes at 10, one step past the last value used.
Worked example
Take a loop written with full assignments and shorten it, checking that nothing about its behaviour changes.
// before
int i = 1;
while (i <= 5) {
System.out.println(i);
i = i + 1;
}
// after
int i = 1;
while (i <= 5) {
System.out.println(i);
i++;
}| pass | i printed | i after |
|---|---|---|
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 3 | 4 |
| 4 | 4 | 5 |
| 5 | 5 | 6 — condition now false |
Identify the update statement.
Why: i = i + 1 at the end of the body.
Replace it with the increment operator.
Why: i++ does exactly the same thing.
Confirm the trace is unchanged.
Why: Five passes printing 1 to 5, and i finishing at 6.
Note what has NOT changed.
Why: The condition, the starting value and the number of passes are all the same. Only the notation is shorter.
Verify: Both versions print 1 through 5 on separate lines.
Why: Then check the final value of i by printing it after the loop: 6 in both cases. A loop variable almost always ends one step past the last value it took, and knowing that prevents a lot of confusion.
Prediction
The loop variable's final value is often needed and often mispredicted.
int i = 0;
while (i < 3) {
System.out.println(i);
i++;
}
System.out.println("i is now " + i);| pass | i printed | i after |
|---|---|---|
| 1 | 0 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| — | condition fails | 3 |
Predict first
What is the last line of output?
Correct: i is now 3
Why: The loop prints 0, 1 and 2, and the increment after the last pass takes i to 3 — which is what fails the condition and ends the loop. A loop variable finishes one step past the last value the body used, which is exactly why i < 3 gives three passes starting from zero.
Concept
i++ saves four characters over i = i + 1. That is not the argument for it.
| form | how many times is the variable named? | risk |
|---|---|---|
| i = i + 1 | twice | you can mistype one of them |
| i++ | once | none of that kind |
| count = count + 1 | twice | in a long name, a typo is easy to miss |
| count++ | once | — |
The real benefit is naming the variable once. total = totl + 1 compiles if totl happens to exist, and is a genuinely nasty bug; total++ cannot go wrong that way. It is the same argument as returning a condition instead of true-or-false in Lesson 5b: prefer the form with fewer places to be wrong.
Trap
The update increments the wrong variable. The loop never ends.
int i = 0;
int total = 0;
while (i < 5) {
total += i;
total++; // meant i++
}| pass | i | total | condition |
|---|---|---|---|
| 1 | 0 | 1 | still true |
| 2 | 0 | 2 | still true |
| ... | 0 | growing | true forever |
i never changes, so i < 5 is always true. The program appears to hang, and the mistake is one character away from a correct loop.
The update must change the variable the condition tests.
int i = 0;
int total = 0;
while (i < 5) {
total += i;
i++;
}| pass | i | total after |
|---|---|---|
| 1 | 0 | 0 |
| 2 | 1 | 1 |
| 3 | 2 | 3 |
| 4 | 3 | 6 |
| 5 | 4 | 10 — then i becomes 5 and the loop ends |
The check to run on every loop you write: name the variable in the condition, then find the statement that changes it. If you cannot point at one, the loop does not terminate.
Matching
Four compound operators.
Match the pairs
Why: Every one of these is an assignment written more briefly — nothing new is happening. ++ and -- change by exactly one, while += and -= take an amount, so i++ and i += 1 are the same thing.
Fill the middle
Print 0, 3, 6, 9.
Fill in the blanks
int i = 0;
while (i <= 9) +=} 3;
}
Why: <= is needed so that 9 itself is printed — with < the loop would stop at 6. += 3 steps by three each pass. Note that != 10 would be a disaster here: i takes the values 0, 3, 6, 9, 12 and never equals 10, giving an infinite loop.
Edge cases
They are described as having the same effect. Push on that.
Discussion prompt
As a statement on its own, i++ and i = i + 1 are identical. Can you think of a context where the two forms might not be interchangeable? What does that suggest about writing i++ inside a larger expression?
Hint: What value does the expression i++ itself have?
Answer:
i++ is an expression as well as a statement, and its value is the value of i before the increment. So int j = i++; gives j the old value, while int j = ++i; gives it the new one.
That distinction is a well-known source of confusing code, and Think Java sensibly does not dwell on it. The practical advice: use i++ as a statement on its own line, and never inside a larger expression. Code that depends on which form you used is code that will be misread.
Section
Section 6.3
Concept
The loops so far have three parts in common: they initialise a variable, they have a condition depending on it, and they update it inside the body. Running the same code multiple times is called iteration, and it is common enough that Java has a statement expressing it more concisely.
for (int i = 2; i <= 8; i += 2) {
System.out.print(i + ", ");
}
System.out.println("Who do we appreciate?");| part | runs | equivalent in a while loop |
|---|---|---|
| int i = 2 | once, at the very beginning | the line before the while |
| i <= 8 | each time through, before the body | the while condition |
| i += 2 | at the end of each iteration | the last statement in the body |
iteration — Executing a sequence of statements repeatedly.
loop variable — A variable that is initialised, tested and updated in order to control a loop.
The for loop is often easier to read because it puts all the loop-related statements at the top, which lets you focus on the statements inside the body.
Picture it
They are written left to right and they do not run in that order after the first pass. The update comes at the end of each iteration.
Figure (svg): A flowchart for a for loop showing initialisation once, then repeated condition, body and update
So the sequence is: initialise, test, body, update, test, body, update, ... and the loop ends at a test. The update runs after every body, including the last one — which is why the loop variable finishes past the end.
Worked example
Any for loop can be rewritten as a while loop and vice versa. Doing the conversion once makes the three parts unmistakable.
// while
int i = 2;
while (i <= 8) {
System.out.print(i + ", ");
i += 2;
}
// for
for (int i = 2; i <= 8; i += 2) {
System.out.print(i + ", ");
}| part | in the while version | in the for version |
|---|---|---|
| initializer | the line above the loop | first slot in the parentheses |
| condition | in the while parentheses | second slot |
| update | the last line of the body | third slot |
| the actual work | the rest of the body | the whole body |
Find the three loop-control pieces in the while version.
Why: Initialisation above, condition in the parentheses, update at the bottom of the body.
Move them into the for header, separated by semicolons.
Why: Note that the separators are semicolons, not commas.
Leave only the real work in the body.
Why: The body now contains nothing about controlling the loop.
Check the trace is identical.
Why: Four passes printing 2, 4, 6 and 8.
Verify: Both versions print 2, 4, 6, 8, Who do we appreciate?
Why: The benefit is visible immediately: in the for version the body is one line and it is the line that does the work. Everything about how many times it runs is in one place, where it can be checked at a glance.
Prediction
Count from the header alone.
for (int i = 0; i < 5; i++) {
System.out.println("hello");
}| i | condition | body runs? |
|---|---|---|
| 0, 1, 2, 3, 4 | true | yes — five times |
| 5 | 5 < 5 is false | no |
Predict first
How many times is 'hello' printed?
Correct: 5
Why: i takes the values 0, 1, 2, 3 and 4 — five values — before reaching 5 and failing the condition. The pattern for (int i = 0; i < n; i++) runs exactly n times, and it is worth learning as a unit because it is the most common loop in all of Java.
Concept
There is one real difference between the two forms. If you declare a variable in the initializer, it exists only inside the for loop.
for (int n = 3; n > 0; n--) {
System.out.println(n);
}
System.out.println("n is now " + n); // compiler error
// declare it outside if you need it afterwards:
int n;
for (n = 3; n > 0; n--) {
System.out.println(n);
}
System.out.println("n is now " + n); // fine| declared | usable inside the loop? | usable after it? |
|---|---|---|
| for (int n = 3; ...) | yes | no — cannot find symbol |
| int n; then for (n = 3; ...) | yes | yes |
Notice that the second version's for statement does not say int n = 3 — it simply initialises the existing variable. This is Lesson 4a's scope idea again: a variable lives in the block that declares it.
Trap
The separators inside a for header are semicolons. Commas do not compile.
for (int i = 0, i < 5, i++) { // syntax error
System.out.println(i);
}| what Java expects | what it found |
|---|---|
| initializer ; condition ; update | three items separated by commas |
| a semicolon after the initializer | a comma |
| result | ';' expected |
The mistake is understandable — three items in parentheses look like arguments, which are comma-separated. But these are three separate statements, and statements are separated by semicolons.
Semicolons, because each part is a statement rather than an argument.
for (int i = 0; i < 5; i++) {
System.out.println(i);
}| slot | contains | kind of thing |
|---|---|---|
| 1 | int i = 0 | a declaration and assignment |
| 2 | i < 5 | a boolean expression |
| 3 | i++ | an assignment |
Reading the header as three statements, not three arguments also explains why the middle one must be a boolean and the other two need not be — they are doing different jobs.
Definition probe
Three slots, separated by semicolons.
Sort into buckets
Sort each piece of a for header.
Prediction
The loop variable is used after the loop.
for (int n = 3; n > 0; n--) {
System.out.println(n);
}
System.out.println("n is now " + n);| where n is declared | where it exists |
|---|---|
| in the initializer | inside the for loop only |
| the line after the loop | outside — n does not exist there |
Predict first
What happens?
Correct: A compile error — n does not exist outside the loop
Why: A variable declared in a for loop's initializer exists only inside that loop, so the last line reports 'cannot find symbol'. To use the value afterwards you must declare the variable before the loop and write for (n = 3; ...) with no type in the initializer.
Trade off
Fill the blanks from what the two forms make easy.
Comparison matrix
| for | while | |
|---|---|---|
| where the loop control lives | all three parts in the header | spread over three places |
| the loop variable's scope | local to the loop, if declared in the initializer | whatever block declared it |
| best when | you know how many repetitions | you do not know how many |
The last row is the rule from Section 6.6, and it is the one worth remembering: definite counting takes a for, indefinite waiting takes a while.
Section
Section 6.4
Concept
Like conditional statements, loops can be nested one inside the other. Nested loops let you iterate over two variables — for example, to generate a multiplication table.
for (int x = 1; x <= 10; x++) {
for (int y = 1; y <= 10; y++) {
System.out.printf("%4d", x * y);
}
System.out.println();
}| loop | called | iterates | how many times in total |
|---|---|---|---|
| for x | the outer loop | 1 to 10, once | 10 |
| for y | the inner loop | 1 to 10, for each x | 100 |
| printf | — | inside the inner loop | 100 |
| println() | — | inside the outer loop | 10 — one newline per row |
The outer loop iterates from 1 to 10 only once, but the inner loop iterates from 1 to 10 each of those ten times. As a result printf is invoked 100 times.
Picture it
It is important to realise that the output is displayed row by row. The inner loop displays a single row; the outer loop iterates over the rows themselves.
Figure (svg): A grid showing the first four rows and columns of a multiplication table with row and column indices
Another way to read nested loops is: for each row x, and for each column y, ... — which is exactly how you would describe filling in the table by hand.
Worked example
The single most important detail in a nested printing loop is which loop the println() belongs to. Moving it one level changes the entire output.
for (int x = 1; x <= 3; x++) {
for (int y = 1; y <= 3; y++) {
System.out.printf("%4d", x * y);
}
System.out.println(); // inside the OUTER loop
}| where println() is | runs how often | output shape |
|---|---|---|
| inside the outer loop, after the inner | 3 times — once per row | a 3 by 3 grid |
| inside the inner loop | 9 times | one number per line |
| after both loops | once | all nine numbers on one line |
Put the printf inside the inner loop.
Why: It produces one cell, and %4d pads it to four characters wide so the columns line up.
Put the println after the inner loop but inside the outer one.
Why: It ends the row, and runs once per value of x.
Check the counts.
Why: printf 9 times, println 3 times — the ratio is the inner loop's length.
Try moving the println and predict first.
Why: Inside the inner loop, every number gets its own line. Outside both, there are no line breaks at all.
Verify: Expect three rows of three numbers, aligned in columns.
Why: The alignment is worth noticing: %4d displays the value padded with spaces to four characters, so the output lines up vertically regardless of how many digits each number has.
Prediction
Multiply.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
System.out.print("*");
}
}| loop | passes |
|---|---|
| outer | 4 |
| inner, each time | 3 |
| total | 12 |
Predict first
How many asterisks are printed?
Correct: 12
Why: The outer loop runs 4 times and the inner loop runs 3 times for each of them, so the inner body runs 4 times 3, which is 12. Nested loops multiply rather than add — adding would be the count for two loops written one after the other.
Concept
Nested loops multiply. That is obvious once stated and routinely underestimated when it matters.
| outer runs | inner runs each time | body of the inner loop runs |
|---|---|---|
| 10 | 10 | 100 |
| 100 | 100 | 10,000 |
| 1,000 | 1,000 | 1,000,000 |
| n | n | n squared |
The last row is the one to carry forward. A nested loop over the same collection does work proportional to the square of its size — which is why doubling the data quadruples the time. Chapter 12's search algorithms are the first place that difference matters.
Trap
The inner loop reuses the outer loop's variable, and the outer loop never finishes properly.
for (int i = 1; i <= 3; i++) {
for (i = 1; i <= 3; i++) { // same variable!
System.out.print(i);
}
System.out.println();
}| what happens | why |
|---|---|
| the inner loop resets i to 1 | it shares the outer loop's variable |
| the inner loop runs i up to 4 | then exits |
| the outer update makes i 5 | the outer condition fails immediately |
| result | one row, not three |
This compiles, because reusing an existing variable in an initializer is legal. It just does not do what a nested loop is supposed to do.
Each loop gets its own variable, declared in its own initializer.
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
System.out.print(j);
}
System.out.println();
}| variable | controlled by | range |
|---|---|---|
| i | the outer loop | 1 to 3 |
| j | the inner loop | 1 to 3, restarted for each i |
Because j is declared inside the outer loop's body, it is created fresh on every outer pass — which is exactly the behaviour you want. Conventional names for nested loop variables are i, j and k.
Prediction
The println has moved inside the inner loop.
for (int x = 1; x <= 2; x++) {
for (int y = 1; y <= 2; y++) {
System.out.print(x * y);
System.out.println();
}
}| pass | printed | then |
|---|---|---|
| x=1, y=1 | 1 | newline |
| x=1, y=2 | 2 | newline |
| x=2, y=1 | 2 | newline |
| x=2, y=2 | 4 | newline |
Predict first
What shape is the output?
Correct: Four lines, one number each
Why: The println is inside the inner loop, so it runs after every single number rather than at the end of each row. To get a grid the println must be inside the outer loop and after the inner one — which is the one line that decides the whole shape of the output.
Invariant
Watch both loop variables at once.
Step through it
How many times does the inner loop variable get reset to 1?
Once per outer pass. That resetting is what makes the inner loop cover its whole range for every value of the outer variable — and it is why the two must be different variables.
Scale up
Watch the number of printf calls as the loop bounds increase.
Step through it
If the size doubles, by what factor does the work grow?
By four. Nested loops over the same collection do work proportional to the square of its size, so doubling the data quadruples the time. This is the first appearance of an idea that Chapter 12 makes central when it compares sequential and binary search.
Section
Sections 6.5-6.6
Concept
Some of the most interesting problems in computer science involve searching and manipulating text. Strings provide a method named charAt, which returns a char — a data type storing an individual character, as opposed to strings of them.
String fruit = "banana";
char letter = fruit.charAt(0); // 'b'
if (letter == 'A') {
System.out.println("It's an A!");
}| literal | quotes | holds |
|---|---|---|
| 'A' | single | exactly one character |
| "A" | double | a string that happens to be one character long |
| '\t' | single | one character — an escape sequence is still one character |
| 'AB' | single | illegal — a char literal holds only one character |
The argument 0 means the character at index 0. String indexes range from 0 to n-1, where n is the length of the string — so the first character is at 0, not 1.
Picture it
Every string method in the next lesson takes an index. Drawing the ruler once now saves a great deal of confusion later.
Figure (svg): The string banana with each character in a numbered box, indices 0 through 5, and the first character highlighted
Note where the numbering starts and where it stops. banana has length 6 and its last character is at index 5 — a fact that causes one specific exception in the next lesson.
Worked example
Characters work like the other data types you have seen. The increment operator works on them, which makes this loop possible.
System.out.print("Roman alphabet: ");
for (char c = 'A'; c <= 'Z'; c++) {
System.out.print(c);
}
System.out.println();| part | value | note |
|---|---|---|
| initializer | char c = 'A' | the loop variable is a char |
| condition | c <= 'Z' | characters compare with relational operators |
| update | c++ | moves to the next character |
| output | ABCDEFGHIJKLMNOPQRSTUVWXYZ | 26 passes |
Declare the loop variable as a char.
Why: The for loop does not require an int — any type that can be compared and updated will do.
Compare characters with <=.
Why: This works because each character has a numeric code point behind it.
Increment with c++.
Why: Which moves to the next code point, and therefore the next letter.
Note why the letters are consecutive.
Why: In Unicode the uppercase Roman letters occupy consecutive code points, so incrementing works. That is a property of the encoding, not a guarantee about all alphabets.
Verify: Expect the 26 uppercase letters with no spaces between them.
Why: Then try starting at 'a' and ending at 'z' for lowercase. If you tried 'A' to 'z' you would get the letters plus the six punctuation characters that sit between the two alphabets in Unicode — which is a good demonstration that the ordering is the encoding's, not the alphabet's.
Prediction
A char loop with a small range.
for (char c = 'a'; c <= 'e'; c++) {
System.out.print(c);
}| c | condition | printed |
|---|---|---|
| 'a' to 'e' | true | abcde |
| 'f' | 'f' <= 'e' is false | loop ends |
Predict first
What is the output?
Correct: abcde
Why: The loop runs from 'a' through 'e' inclusive because the condition uses <=, and print adds nothing between the characters. Lowercase letters occupy consecutive Unicode code points, which is what makes incrementing a char step through the alphabet.
Concept
Java uses Unicode to represent characters, so strings can store text in other alphabets — Cyrillic, Greek — and non-alphabetic languages like Chinese. Each character is represented by a code point, which you can think of as an integer.
System.out.print("Greek alphabet: ");
for (int i = 913; i <= 937; i++) {
System.out.print((char) i);
}
System.out.println();| code point | character |
|---|---|
| 65 | A |
| 913 | the first uppercase Greek letter |
| 937 | the last one |
| (char) 913 | converts the int to its character |
Unicode — An international standard for representing characters in most of the world's languages.
The cast (char) i is Lesson 3b's type cast doing a new job: converting each integer in the range to the corresponding character. Casting works here because the types are compatible — a char really is a number underneath.
Trap
A for loop where the number of repetitions is unknown. It cannot express the job.
// how many times will the user type something invalid?
for (int i = 0; i < ???; i++) {
// there is no sensible bound to write here
}| situation | how many repetitions? | can a for header say it? |
|---|---|---|
| print the alphabet | 26 — known in advance | yes |
| validate user input | unknown — depends on the user | no |
| iterate a string's characters | its length — known | yes |
A for loop is definite: you know at the beginning how many times it will repeat. When you do not, the header has nothing to put in its condition slot except a flag, which defeats the purpose.
A while loop is indefinite — use it when the repetition count is not known in advance.
System.out.print("Enter a number: ");
while (!in.hasNextDouble()) {
String word = in.next();
System.err.println(word + " is not a number");
System.out.print("Enter a number: ");
}
double number = in.nextDouble();| kind | means | use for |
|---|---|---|
| for — definite | you know how many repetitions at the start | counting, iterating a string, a fixed range |
| while — indefinite | you do not know how many | validating input, reading until a sentinel, searching |
Compare this with Lesson 5b's validation, which gave up after one bad input. This version keeps asking until the user types something usable — a genuine improvement, and only a while loop can express it. It is also easier to read the Scanner calls when they are not all on one line.
Definition probe
Ask whether you know the number of repetitions before the loop starts.
Sort into buckets
Sort each task by the loop that fits.
Discrimination
Single quotes hold one character; double quotes hold a string.
Sort into buckets
Sort each literal.
Counterexample
The letters are consecutive. Test how far that goes.
Discussion prompt
for (char c = 'A'; c <= 'Z'; c++) prints the uppercase alphabet. What would for (char c = 'A'; c <= 'z'; c++) print — and what does the answer tell you about what 'consecutive' really means here?
Hint: There are 26 uppercase and 26 lowercase letters. How many code points lie between 'A' and 'z'?
Answer:
It prints 58 characters: the 26 uppercase letters, then six punctuation characters, then the 26 lowercase letters. The two alphabets are consecutive within themselves but are not adjacent to each other.
So c++ on a char is not really 'next letter' — it is next code point, and the letters happen to be laid out conveniently. That distinction matters as soon as you leave the Roman alphabet, and it is why the Greek example in this section loops over integers and casts rather than incrementing a char.
The general habit: when something works, ask whether it works because of the thing you are relying on or because of a coincidence of the representation.
Comparison
They have the same capabilities. Fill the blanks to see where each is clearer.
Comparison matrix
| while | for | |
|---|---|---|
| initialization | on the line before the loop | the first slot in the header |
| update | usually the last statement of the body | the third slot in the header |
| loop variable scope | whatever block declared it | local to the loop, if declared in the initializer |
| kind of repetition | indefinite — you do not know how many | definite — you know how many |
Any for loop can be rewritten as a while loop and vice versa, so the choice is entirely about which one makes the intent obvious to a reader.
Pattern
Every loop has the same three moving parts, whichever form you write it in — and checking all three is how you know it terminates.
// the three parts, in a for loop
for (int i = 0; i < n; i++) {
// body
}
// the same three parts, in a while loop
int i = 0; // initialize
while (i < n) { // condition
// body
i++; // update
}| check before running any loop | if it is missing |
|---|---|
| is the loop variable initialised? | the loop may not compile, or starts from anything |
| does the condition mention it? | the loop cannot depend on the variable's progress |
| does something update it? | an infinite loop |
| does the update move TOWARD the exit? | an infinite loop, in the other direction |
for (int i = 0; i < n; i++) runs exactly n times.Check
Work it out before you click.
int count = 0;
for (int i = 3; i <= 9; i += 3) {
count++;
}| i | condition | body runs? |
|---|---|---|
| 3 | true | yes |
| 6 | true | yes |
| 9 | true | yes |
| 12 | false | no |
Check your understanding
What is count after the loop?
Answer: A
Why: i takes the values 3, 6 and 9 — three passes — and then becomes 12, which fails the condition. The <= matters here: with < the loop would stop at 6 and count would be 2.
Check
Work it out before you click.
int n = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 4; j++) {
n++;
}
}| loop | passes |
|---|---|
| outer | 5 |
| inner, per outer pass | 4 |
| total | 20 |
Check your understanding
What is n after both loops?
Answer: A
Why: The inner loop runs 4 times for each of the outer loop's 5 passes, so the inner body runs 5 times 4, which is 20. Nested loops multiply — adding would be the answer for two loops written one after the other rather than one inside the other.
Check
Work it out before you click.
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
System.out.println(i);| line | is i in scope? |
|---|---|
| inside the loop body | yes |
| the line after the loop | no |
Check your understanding
What happens when you compile this?
Answer: A
Why: A variable declared in a for loop's initializer exists only inside that loop, so the final println refers to a name that is out of scope. Declaring int i; before the loop and writing for (i = 0; ...) would make the last line legal, and it would print 3.
Real world
Section 6.1 slips in a genuinely open mathematical problem, and it is worth taking seriously.
Discussion prompt
The Collatz loop halves n when it is even and computes 3n+1 when it is odd, stopping at 1. It has been checked for enormous starting values and always stops. Why is that not a proof — and what does the situation tell you about reasoning about loops in general?
Hint: How many starting values are there?
Answer:
Because there are infinitely many starting values, and checking any finite number of them says nothing about the rest. This is the Collatz conjecture, and nobody has proved or disproved it.
The general lesson is the one Think Java draws: in general it is not easy to tell whether a loop terminates. For the countdown you can prove it — n decreases by one each pass and the condition tests against a fixed bound. For Collatz you cannot, because n sometimes increases.
What to do about it in practice: prefer loops whose variable moves monotonically toward the exit condition, because those you can check at a glance. When a loop's variable moves both ways, you are in territory where testing is not proof — and that is worth knowing before you rely on it.
Commit first
Commit to an answer and to your confidence.
Predict first
How many times does the body of for (int i = 1; i <= 5; i++) run?
Correct: 5
Why: i takes the values 1, 2, 3, 4 and 5, and the body runs for each of them; on the sixth test i is 6 and the condition fails. Note the contrast with for (int i = 0; i < 5; i++), which also runs five times but with i taking 0 through 4 — both are five passes, and which one you want depends on whether you are counting or indexing. Getting these two straight now matters enormously in the next lesson, where the index of the last character is length minus one.
Explain it
Two minutes, out loud, with a table.
Discussion prompt
A classmate's loop prints one fewer number than they expect. Teach them how to trace a loop on paper — what the columns should be, and what to write in each row. Then explain why a loop variable ends up one step past the last value used.
Hint: The table needs a column for the value at the TOP of each pass.
Answer:
Make a table with one row per pass. The columns are: the value at the top of the pass, whether the condition holds, what the body does, and the value at the end of the pass. The last row is the one where the condition fails and the body does not run.
And the variable ends past the end because the update runs after every body, including the last one. So the final update pushes it to the value that fails the condition — that failing value is what stops the loop.
If the table has no row where the condition is false, it is incomplete. That row is the one that explains both the number of passes and the final value.
Exit ticket
One question before you close the deck.
Predict first
When should you choose a while loop over a for loop?
Correct: When you do not know in advance how many times the loop will repeat
Why: A for loop is definite — you know at the beginning how many repetitions there will be, and the header can state it. A while loop is indefinite, which is what you need for validating input, reading until a file ends, or searching until something is found. The two forms have identical capabilities, so the choice is entirely about which one makes the intent visible: counting downwards, a long body and a char loop variable are all perfectly comfortable in a for loop.
Connect it up
One page, from memory.
Draw it
Draw a flowchart for a while loop, marking the point where the condition is tested and the arrow that loops back. Beside it, write the same loop as a for loop and label its three parts. Then make a four-column trace table for for (int i = 1; i <= 4; i++) including the final row where the condition fails, and write down the value of i on that row. Finally, write the three questions you would ask to check that a loop terminates.
Recap
Six sections that add the last of the five basic instructions, and the tools for writing it so that it stops.
| if you remember one thing | it is this |
|---|---|
| about termination | find the statement that changes the condition's variable |
| about counting | for (int i = 0; i < n; i++) runs exactly n times |
| about choosing | do you know how many? for. Not sure? while. |
++, --, += and -= are assignments written concisely, and they name the variable once.for is definite, while is indefinite — choose by whether you know the count in advance.Want this taught 1-on-1? Alexander tutors Java — $55/session, free consultation.