Infinite Loops and Off-by-One Errors

A loop that never ends
At an airport check-in, a bag over 23 kilograms is too heavy, and items come out of it until it isn't.
int weight = 27;
int removed = 0;
while (weight > 23)
{
removed = removed + 1;
}
The body counts each item removed, but nothing in it changes weight. It holds 27 at every check, so weight > 23 is always true, and the loop never ends.
Changing the right thing
A loop usually ends because something in its body changes a value the Boolean expression reads. Suppose each item removed weighs 2 kilograms.
int weight = 27;
while (weight > 23)
{
System.out.println("Remove an item");
weight = weight - 2;
}
The weight goes from 27 to 25 to 23. At 23, weight > 23 fails. The message has printed twice, and weight holds 23.
A change to the variable isn't enough by itself; it has to be able to make the expression false. Written with weight != 22, the same loop sees 27, 25, 23, 21, 19, and so on. The weight steps over 22 without ever equaling it, so the loop runs forever. A comparison like > stops the loop wherever the weight lands, so it's the safer choice here.
One too many, one too few
The rule is that a bag of 23 kilograms is allowed. Written with weight >= 23, the loop also runs when the weight is exactly 23. The message prints a third time and the weight drops to 21, one item more than the rule requires.
Off-by-one errors often come from the value the variable starts at, or from whether the expression accepts the boundary value itself. To check a loop, trace it at the boundary, here at 23.
A value that marks the end
Some loops read values one at a time without knowing in advance how many there are. The last value is a sentinel, a value chosen to mean the data has ended, and it isn't part of the data.
At the check-in desk, each bag gets a tag letter, S for standard or O for oversize, and the day's tags arrive as a string of unknown length that ends with a period.
String tags = "SOSSO.";
int pos = 0;
int oversize = 0;
String tag = tags.substring(pos, pos + 1);
while (!tag.equals("."))
{
if (tag.equals("O"))
{
oversize++;
}
pos++;
tag = tags.substring(pos, pos + 1);
}
Reading the first tag before the loop gives the opening test an actual tag to look at. Inside the body, the current tag is used first and the next one is read last, so the period ends the loop without being counted as a bag. Here oversize ends at 2.