Chapter 6: Statements and Scope
In 1966 two Italian mathematicians, Corrado Böhm and Giuseppe Jacopini, published a short paper with a surprising claim. Anything a computer can compute, they showed, can be written using only three ways of arranging statements: doing one thing after another, choosing between alternatives, and repeating a group of statements while some condition holds. Nothing more is needed.
At the time, this looked like a mathematical curiosity. Real programs were written with jumps — an instruction that threw control to a labelled line anywhere else in the program. It was fast to write and, on a listing of a few hundred lines, almost impossible to follow.
Two years later, the Dutch computer scientist Edsger Dijkstra published a letter arguing exactly that: unrestricted jumps make a program hard for a human being to reason about, because you can never be sure how control arrived at a given line. The argument won. Every language you are likely to meet today, Java included, is built from the three shapes in Fig. 6.0-1. Java still keeps the word goto on its list of reserved words — and still refuses to let you write it.
So a Java program has no jumps to arbitrary lines. It has statements, blocks, decisions and loops, and that is enough for everything in this book.
Diagram
Fig. 6.0-1 – the three structured forms drawn side by side as flow shapes; left: sequence, three boxes stacked with downward arrows; centre: selection, a diamond labelled ‘condition?’ with a True branch to one box and a False branch to another, both rejoining below; right: iteration, a diamond labelled ‘condition?’ with the True branch entering a box whose arrow returns to the diamond, and the False branch leaving downwards; caption each shape ‘sequence’, ‘selection’, ‘iteration’
Keep these five questions in mind as you read. By the end of the chapter you will be able to answer every one of them.
6.1 Statements in a Java Program
A program is not a paragraph of English that the machine somehow understands. It is a list of instructions, each one complete in itself, each one written in a form the compiler can check. Those instructions are called statements.
Almost everything you will write in this book is a statement of one kind or another, so it is worth being precise about what a statement is, which kinds exist, and which of them actually do something when the program runs.
6.1.1 What a Statement Is
A statement is one complete instruction in a program. Reading a mark, adding two numbers, printing a line, deciding between two courses of action — each of these is written as a statement.
Every statement is built out of tokens. A token is the smallest meaningful piece of a program: a keyword such as class, a name such as total, a literal such as 78, an operator such as +, or a punctuation mark such as ;. The compiler first splits your file into tokens, then checks whether those tokens form legal statements.
The spaces, tabs and blank lines between tokens are called whitespace. Java needs whitespace only where two tokens would otherwise run together, so int total; and int total; mean exactly the same thing. Indentation is therefore for the human reader, not for the compiler — which is precisely why you must be disciplined about it.
A simple statement ends with a semicolon. The semicolon is not decoration; it is how you tell the compiler that one instruction has finished and the next is about to begin.
6.1.2 Executable and Non-Executable Statements
Not every line in a program turns into machine instructions. Some lines are directions to the compiler, or notes to a future reader, and they leave no trace in the running program at all. This gives the first useful classification, shown in Fig. 6.1-1.
Diagram
Fig. 6.1-1 – classification tree; top box ‘Statements in Java’; two branches below it, ‘Executable statements’ and ‘Non-executable statements’; under ‘Executable statements’ four boxes: ‘Input / output’, ‘Assignment’, ‘Conditional’, ‘Looping’; under ‘Non-executable statements’ two boxes: ‘Declaration’, ‘Comment’; all boxes joined to their parent by plain lines
An executable statement is one that produces machine code and does something when the program runs — it computes a value, stores it, displays it, or decides what happens next. A non-executable statement produces no action of its own; it supplies information, either to the compiler or to the person reading the program.
Table 6.1 sets out the kinds you will meet in this chapter.
| Kind of statement | What it is for | Example | Acts at run time? |
|---|---|---|---|
| Declaration | Introduces a name and fixes its type | int marks; |
No |
| Assignment | Stores a value in a variable | marks = 78; |
Yes |
| Input / output | Brings a value in, or sends a result out | System.out.println(marks); |
Yes |
| Conditional | Chooses which statements run | if (marks >= 40) ... |
Yes |
| Looping | Repeats a group of statements | while (i <= 10) ... |
Yes |
| Comment | Explains the code to a reader | // fine in rupees |
No |
Two entries in that table deserve a word of caution.
First, an output statement such as System.out.println(marks); is really a call to a method that belongs to the standard library. It is listed separately because sending values in and out is such a common job, not because Java treats it as a special kind of statement.
Second, the line between “executable” and “non-executable” is not quite as sharp as the table suggests. A declaration on its own reserves a name and a type, and that is a compile-time matter. But a declaration is allowed to carry an initial value with it, and storing that value is an action like any other. Written as int marks = 78; the line is doing two jobs at once.
6.1.3 Declaration Statements
A declaration statement introduces a name into the program and fixes what kind of value that name may hold.
int daysLate;
double ratePerDay;
char grade;Java is a strongly typed language: every variable has a type, the type is fixed when the variable is declared, and the compiler refuses any statement that would put the wrong kind of value into it. Assigning "nine" to an int is not an error that shows up when the program runs — the program never gets as far as running.
This is why a declaration must appear before the first use of the name. The compiler reads your file in order, and it cannot check a statement about daysLate until it knows that daysLate exists and is an int.
Several variables of the same type may be declared together, separated by commas, and any of them may be given a starting value at the same time:
int daysLate = 6, booksIssued = 2;You met named constants in Class XI: a variable declared with final may be assigned once and never changed afterwards, which is the right way to write a fixed rate or a fixed limit. Use it whenever a value has a name but no reason to vary.
6.1.4 Assignment Statements
An assignment statement stores a value in a variable. It is written with a single equals sign, and it is read from right to left: work out the value on the right, then put it into the variable named on the left.
fine = daysLate * ratePerDay;The two sides of an assignment are not interchangeable, and they do not even mean the same kind of thing. The right-hand side is an expression — something that has a value. The left-hand side must be a variable — something that has a place to store a value. This is why daysLate = 6; is legal and 6 = daysLate; is not: the number six has a value, but it has nowhere to put one.
A variable may appear on both sides at once, and beginners often find this line unsettling:
count = count + 1;Read as mathematics it is nonsense — no number equals itself plus one. Read as an instruction it is perfectly ordinary: take the value currently in count, add one to it, and store the result back in count. The right-hand side is evaluated first, using the old value; only then is the new value stored. This one idea is what makes counting inside a loop possible, and you will use it in almost every loop in this chapter.
Because that pattern is so common, Java provides shorter forms. The compound assignment operators +=, -=, *=, /= and %= combine an operation with an assignment, so count += 1; does exactly what count = count + 1; does. For the special case of adding or subtracting one there are the increment and decrement operators, count++ and count--.
6.2 Blocks, Scope and Visibility
Statements do not float about loose in a program. They are gathered into groups, and those groups decide two things at once: which statements are governed together, and where each name may be used.
Both ideas are carried by the same pair of symbols, the braces { and }. Get them right and the structure of a program is visible at a glance. Get them wrong and the program will often still compile — and quietly do the wrong thing.
6.2.1 Grouping Statements in a Block
A block is a group of statements enclosed in braces. Java treats the whole group as though it were a single statement.
That last sentence is the point of blocks. Java’s conditional and looping statements each govern exactly one statement — one, no more. When you need three statements to run under a condition, you do not change the condition; you wrap the three statements in braces so that, as far as the language is concerned, they are one.
Blocks nest. The body of a class is a block, the body of a method is a block inside it, and a block may be written inside a method wherever a single statement is allowed:
class Outline
{
public static void main(String args[])
{
System.out.println("first");
{
System.out.println("inside an inner block");
}
System.out.println("last");
}
}A block may hold no statements at all. An empty pair of braces, { }, is a perfectly legal block that does nothing, and you will meet a use for it later in this chapter.
6.2.2 Scope of a Variable
The scope of a variable is the region of the program text in which its name may be used. Outside that region the name simply does not exist, and any statement that mentions it will be rejected by the compiler.
The rule for a variable declared inside a method is short: its scope begins at its declaration and ends at the closing brace of the block that contains it. Such a variable is called a local variable — local, that is, to the block it was declared in.
Two consequences follow, and Fig. 6.2-1 shows both.
Diagram
Fig. 6.2-1 – scope regions; a large rectangle labelled ‘method body’ containing, from top to bottom, the line ‘int total;’ and, below it, a smaller rectangle labelled ‘inner block’ containing the line ‘int discount;’; a vertical bar drawn beside the code from the line ‘int total;’ down to the bottom edge of the large rectangle, labelled ‘scope of total’; a shorter vertical bar beside ‘int discount;’ running only to the bottom edge of the inner rectangle, labelled ‘scope of discount’; the two bars overlap over the inner block
The first consequence is that an inner block can see the names declared outside it. Inside the inner block of Fig. 6.2-1, both total and discount are in scope, so a statement there may use either.
The second is that the outer block cannot see inwards. After the inner block has closed, discount is gone. A statement that mentions it below that point is not a run-time failure — the program will not compile.
The program below puts the idea to work. A temporary value is needed only while a discount is being applied, so it is declared inside a block and disappears when that block ends.
6.2.3 Lifetime and Visibility
Scope and lifetime are easy to confuse because they usually coincide, but they are answers to different questions. Scope is a question about the text of your program: where may this name be written? Lifetime is a question about the running program: for how long does this variable’s storage exist?
A local variable comes into existence when its declaration is executed and ceases to exist when control leaves the block it belongs to. If control enters that block again, a fresh variable is created, with no memory of the previous one.
That is a small statement with a large consequence. A variable meant to accumulate a running total across repeated passes must be declared outside the repeated block, because a variable declared inside it is destroyed and rebuilt every pass, and its accumulated value is lost each time. You will meet this in section 6.6 as one of the commonest reasons a loop returns the wrong answer.
| Feature | Scope | Lifetime |
|---|---|---|
| What it describes | A region of the program text | A period during execution |
| When it is fixed | At compile time | At run time |
| For a local variable | From its declaration to the end of its block | From the moment its declaration runs to the moment control leaves the block |
| What violating it causes | The program does not compile | The stored value is lost and rebuilt |
| Checked by | The compiler | Nothing — you must reason about it yourself |
Visibility is the related idea of which names a given statement can actually reach. Every statement can reach the names declared before it in its own block, and every name in scope in the blocks enclosing it — no others. This is why a well-chosen block is a form of protection: a name that is never visible where it does not belong can never be misused there.
Variables declared in the body of a class, rather than inside a method, follow a wider rule: they belong to the class and are visible throughout it, so more than one method can use them. That treatment belongs with objects and classes and is taken up there; everything in this chapter uses local variables only.
6.3 The Flow of Control
At any instant while a program is running, exactly one statement is being carried out. The order in which statements take their turn is called the flow of control, and shaping that order is what the rest of this chapter is about.
Left to itself the flow is simple and rigid. What makes a program more than a list of instructions is the small set of statements that are allowed to redirect it.
6.3.1 Sequential Flow
When execution begins, control passes to the first statement of main. That statement is carried out, then the next, then the next, in exactly the order in which they are written, until the end of the method is reached. This is the default, and it is called sequential flow.
Sequential flow is worth stating explicitly because it is the assumption behind every trace you will ever do by hand. It also explains why the order of two assignments is never a matter of taste:
int price = 40;
int quantity = 3;
int total = price * quantity;
price = 50;
System.out.println(total);The printed value is 120, not 150. By the time price is changed, total has already been computed and stored; nothing goes back and recalculates it. An assignment captures the value that existed at the moment it ran.
Fig. 6.3-1 shows the same idea as a path through a listing.
Diagram
Fig. 6.3-1 – a five-line program listing drawn as five stacked boxes, numbered 1 to 5 on the left; a single arrow enters box 1 from above, then short arrows join box 1 to 2, 2 to 3, 3 to 4, 4 to 5, and an arrow leaves box 5 downwards; label the entering arrow ‘control enters’ and the leaving arrow ‘control leaves’; caption ‘each statement runs once, in written order’
6.3.2 Selection and Iteration
A control statement is a statement whose job is not to compute a value but to decide which statements run next. Java’s control statements fall into two families.
Selection chooses between alternatives. A condition is tested, and one group of statements is carried out while another is skipped. Whatever the choice, control moves on afterwards; nothing is repeated. The if statement and its variants, the ternary operator and the switch case statement are all selection statements, and they are the subject of sections 6.4 and 6.5.
Iteration repeats. A group of statements is carried out again and again while a condition remains true, and control returns to the same place each time. The for, while and do while statements are Java’s looping statements, and they are the subject of sections 6.6 to 6.8.
| Feature | Selection | Iteration |
|---|---|---|
| What it decides | Whether a group of statements runs | How many times a group of statements runs |
| Times the governed statements can run | At most once for each alternative | Zero, one, or many times |
| Where control goes after the condition is false | Onward, past the statement | Onward, out of the loop |
| Does control ever come back to the condition? | No | Yes, before every pass |
| Typical use | Grading a mark, choosing a rate of discount | Totalling a list, repeating a calculation until it converges |
Both families are driven by the same thing: a condition, which is an expression whose value is either true or false. In Java such an expression must be of type boolean. This is stricter than some other languages, where any non-zero number counts as true, and the strictness is deliberate — it turns a whole family of accidental errors into compile-time failures rather than silent wrong answers.
Sections 6.4 to 6.8 take these statements one at a time. Section 6.7 then deals with break and continue, which do not start a new kind of flow but interrupt an existing loop in two carefully limited ways.
6.4 Conditional Statements
Almost nothing worth computing can be computed the same way for every input. A fare depends on the distance, a fine depends on the number of days, a message depends on whether a value is valid. The program must look at the data and choose.
A conditional statement is a control statement that tests a condition and, on the result, decides which statements are carried out and which are skipped.
Java gives four forms of this idea — if, if else, the if else if ladder and the nested if — together with an operator, the ternary operator, that does the same job inside an expression. This section takes them in that order.
6.4.1 The if Statement
The simplest form does one thing or nothing:
if (condition)
statementIf the condition is true, the statement runs and control moves on. If it is false, the statement is skipped and control moves on anyway. Fig. 6.4-1 shows the two paths.
Diagram
Fig. 6.4-1 – flow diagram for the if statement; an arrow enters a diamond labelled ‘condition true?’; the ‘Yes’ branch leaves to the right into a box labelled ‘statement’; the ‘No’ branch leaves downwards; the arrow out of the box rejoins the ‘No’ path below; a single arrow continues downwards labelled ‘next statement’
Here it is on a real test. A student whose attendance has fallen below the required minimum should be warned:
if (attendance < 75)
System.out.println("Attendance is short of the requirement");Notice the round brackets around the condition. They are compulsory in Java, and they are part of the statement’s syntax, not part of the expression.
The one-statement rule
An if governs exactly one statement. That statement may be a simple statement, or it may be a block — but it is one statement either way, and the compiler decides which one purely from the punctuation, never from the layout.
This is where a great deal of quiet damage is done. Consider:
if (attendance < 75)
System.out.println("Attendance is short of the requirement");
System.out.println("Please meet the class teacher");The indentation says that both lines are governed by the condition. The language says otherwise: the if governs the first line only, and the second line is an ordinary statement that follows it. Every student, however good their attendance, is asked to meet the class teacher.
Braces settle it:
if (attendance < 75)
{
System.out.println("Attendance is short of the requirement");
System.out.println("Please meet the class teacher");
}Many programmers write the braces even around a single statement, so that adding a second line later cannot introduce this bug. It is a habit worth forming.
6.4.2 The if else Statement
When there are two courses of action and one of them must be taken, use if else:
if (condition)
statement1
else
statement2Exactly one of the two runs — never both, and never neither. Fig. 6.4-2 shows the two branches meeting again below.
Diagram
Fig. 6.4-2 – flow diagram for the if else statement; an arrow enters a diamond labelled ‘condition true?’; the ‘Yes’ branch goes right into a box labelled ‘statement 1’; the ‘No’ branch goes left into a box labelled ‘statement 2’; arrows from both boxes meet at a single point below the diamond and continue downwards as one arrow
A number is either even or odd, and there is no third possibility, so the test is naturally written this way:
if (n % 2 == 0)
System.out.println("Even");
else
System.out.println("Odd");The else part has no condition of its own. It is reached precisely when the condition of its if is false, so writing else if (n % 2 != 0) here would add a test whose answer is already known.
6.4.3 The if else if Ladder
Some choices have more than two outcomes: a fare band, a grade, a rate of discount. For these, the else part of one conditional is itself another conditional. Written out and lined up, the result is called an if else if ladder:
if (condition1)
statement1
else if (condition2)
statement2
else if (condition3)
statement3
else
statement4The conditions are tested strictly in order, from the top. The moment one of them is true, its statement runs and the whole ladder is finished — every remaining condition is skipped, not merely found false. The final else, if present, catches everything that reached the bottom untested.
Diagram
Fig. 6.4-3 – flow diagram for an if else if ladder with three conditions; a vertical column of three diamonds labelled ‘condition 1?’, ‘condition 2?’, ‘condition 3?’, each ‘No’ branch dropping to the next diamond and the last ‘No’ branch reaching a box labelled ‘statement 4’; each ‘Yes’ branch leaving to the right into boxes labelled ‘statement 1’, ‘statement 2’, ‘statement 3’; arrows from all four boxes joining a single vertical line on the right that continues downwards
That “first true condition wins” behaviour is what makes a ladder convenient for slabs, and it is also what makes the order of the tests critical.
A ladder is not the same as several separate ifs
Written as three independent conditionals rather than as a ladder, the same tests behave quite differently:
if (distance <= 5)
fare = 400;
if (distance <= 10)
fare = 600;
if (distance <= 20)
fare = 850;Every one of these is tested, because none of them is attached to the one above. For a distance of 3 km all three conditions are true, fare is assigned three times, and the value that survives is the last one — 850. Separate if statements are the right choice only when the conditions are genuinely independent of one another and more than one of them may reasonably apply.
6.4.4 Nested if Statements
A nested if statement is an if written inside the if part or the else part of another. It is used when the second question is worth asking only after the first has been answered.
Finding the largest of three values is the standard example. Once you know which of the first two is larger, only that one needs to be compared with the third; the other has already been ruled out.
Diagram
Fig. 6.4-4 – decision tree for the largest of three values; root diamond labelled ‘a > b?’; its ‘Yes’ branch leads to a diamond labelled ‘a > c?’ whose ‘Yes’ branch reaches a leaf ‘largest = a’ and whose ‘No’ branch reaches a leaf ‘largest = c’; its ‘No’ branch leads to a diamond labelled ‘b > c?’ whose ‘Yes’ branch reaches a leaf ‘largest = b’ and whose ‘No’ branch reaches a leaf ‘largest = c’; label the two levels ‘first comparison’ and ‘second comparison’
Each path through the tree makes exactly two comparisons, and every path ends by assigning a value — which is what makes the method correct for every input.
6.4.5 The Ternary Operator
The ternary operator compresses a two-way choice into an expression. It is written with a question mark and a colon, and it takes three operands — which is where its name comes from:
condition ? valueIfTrue : valueIfFalseThe condition is evaluated; the whole expression then takes one of the two values. Because it is an expression and not a statement, it has a value and can appear wherever a value is expected:
int max = (a > b) ? a : b;Only one of a and b is evaluated, and the other is left alone. The same choice written with if else needs four lines and a variable that is declared first and assigned later:
int max;
if (a > b)
max = a;
else
max = b;| Feature | if else statement |
Ternary operator |
|---|---|---|
| What it is | A statement | An expression |
| Does it produce a value? | No | Yes — the whole expression has a value |
| What each branch may contain | Any statement or block | A value only |
| Can it be used inside another expression? | No | Yes, for example inside a println |
| Suits | Two different courses of action | Two different values for one thing |
That last row is the honest test of when to use it. If both branches end by giving a value to the same variable, the ternary operator says so more clearly than four lines can. If the branches do different work — print different reports, read different inputs — it is the wrong tool, because a value is all it can produce.
Nesting one ternary operator inside another is legal, and it quickly becomes unreadable. A ladder of three or more outcomes is almost always better written as an if else if ladder.
6.5 The switch case Statement
An if else if ladder can express any choice at all, but it is clumsy when the choice has one particular shape: a single value compared against a list of fixed alternatives. A menu option, a month number, a code letter — in each case the same expression is tested again and again for equality against different constants.
The switch case statement is built for exactly that shape. It evaluates one expression, finds the label that matches its value, and jumps there.
6.5.1 Structure of a switch case Statement
The general form is:
switch (expression)
{
case constant1:
statements
break;
case constant2:
statements
break;
default:
statements
}The expression in brackets is evaluated once. Its value is then matched against each case label, and control jumps to the label that matches. If none matches, control jumps to default; if there is no default, the whole statement is skipped. Fig. 6.5-1 shows the shape of the flow.
Diagram
Fig. 6.5-1 – flow diagram for a switch case statement; an arrow enters a box labelled ‘evaluate the switch expression’; from it four labelled arrows fan out to the right into boxes ‘case 1 statements’, ‘case 2 statements’, ‘case 3 statements’ and ‘default statements’, the arrows labelled ‘value = 1’, ‘value = 2’, ‘value = 3’ and ‘no match’; each of the first three boxes has an arrow labelled ‘break’ reaching a single vertical exit line on the far right; the default box joins the same exit line; the exit line continues downwards, labelled ‘statement after the switch’
Two restrictions follow from the way this construct works.
First, the expression must be of a type that can be matched exactly. Java allows byte, short, char and int, the wrapper classes corresponding to them, and enumerated types; from Java 7 onwards a String is allowed as well. It does not allow long, float, double or boolean. A switch on a fractional value would rest on exact equality between values that are only approximations, and a switch on a boolean is an if else written the long way.
Second, every case label must be a constant known at compile time, and no two labels in the same switch may carry the same value. A label cannot be a variable, and it cannot be a range or a condition.
Here is a library menu written this way:
switch (choice)
{
case 1:
System.out.println("Issue a book");
break;
case 2:
System.out.println("Return a book");
break;
case 3:
System.out.println("Renew a book");
break;
}6.5.2 The break Statement and Fall Through
The single most important thing to understand about switch case is that case labels are entry points, not compartments. Control jumps to the matching label and then carries straight on, through any labels that follow, until something stops it.
The thing that stops it is the break statement. A break inside a switch transfers control out of the whole switch block, to the statement that follows it.
When a break is missing, control runs on into the next case. This behaviour is called fall through. Look at what it does here:
int choice = 2;
switch (choice)
{
case 1:
System.out.println("Pen");
case 2:
System.out.println("Notebook");
case 3:
System.out.println("Eraser");
break;
case 4:
System.out.println("Scale");
break;
}The output is:
Notebook
Eraser
Control entered at case 2, printed Notebook, found no break, fell through into case 3 and printed Eraser, and only there met a break that took it out of the block. Pen was never printed, because control never entered above the matching label — fall through runs downwards only.
Fall through is not always an accident. When several values are to be treated in exactly the same way, their labels can be stacked one above the other with no statements in between, so that all of them lead to the same code:
case 4:
case 6:
case 9:
case 11:
days = 30;
break;Here the fall through is deliberate and is doing useful work. Written any other way, those four values would need four copies of the same statement.
6.5.3 The default Case
The default case is the label control jumps to when the switch expression matches none of the case labels. It plays the part that the final else plays in a ladder, and for the same reason: real input is not confined to the values you expected.
By convention default is written last, and when it is last it needs no break, since control is already at the end of the block. It may legally be placed elsewhere, and then it needs a break like any other case — which is one more reason to leave it at the bottom.
The program below uses both ideas of this section: stacked labels for values that share an answer, and a default for values that are not months at all.
6.5.4 switch case and the if else if Ladder Compared
Both constructs choose one path out of several, and for the menu example either would serve. They are not interchangeable in general.
| Feature | switch case |
if else if ladder |
|---|---|---|
| What is tested | One expression, matched for equality against constants | A separate condition at each rung |
| Values allowed | byte, short, char, int, their wrapper classes, enumerated types, and String from Java 7 onwards |
Any condition of type boolean |
| Ranges and compound conditions | Cannot be expressed | Expressed directly |
| What happens after a match | Control continues into the following cases unless a break stops it |
The remaining rungs are skipped automatically |
| Catch-all branch | default |
The final else |
| Reads best when | Several fixed values are to be matched | The tests are ranges, or involve more than one variable |
The practical rule is short. If you are testing one variable against a handful of exact values, a switch states the whole set of possibilities at a glance. If the tests involve ranges, or more than one variable, a ladder is the only honest choice — and forcing such a test into a switch by inventing a label for every possible value is a sign that the wrong construct has been chosen.
6.6 Looping Statements
Selection lets a program skip work. Repetition lets it do the same work many times over without that work being written many times over, and this is what turns a page of code into something that can handle a class of forty, a ledger of ten thousand entries, or a calculation refined until it is accurate enough.
A loop is a control statement that carries out a group of statements repeatedly while a condition remains true.
Java provides three looping statements — for, while and do while. They differ in where the condition is tested and in how tidily the bookkeeping is arranged, not in what they can compute; anything one of them can do, the others can do too.
6.6.1 The Four Parts of a Loop
Every working loop, whichever statement is used to write it, contains the same four parts. Fig. 6.6-1 shows how they fit together.
Diagram
Fig. 6.6-1 – the cycle of a loop; a box labelled ‘initialisation’ at the top with an arrow down into a diamond labelled ‘test condition’; the ‘True’ branch of the diamond leads down into a box labelled ‘body of the loop’, which leads into a box labelled ‘update’; an arrow returns from ‘update’ up to the diamond, closing the cycle; the ‘False’ branch leaves the diamond to the right, labelled ‘exit the loop’
The initialisation gives a starting value to the variable that controls the repetition. That variable is called the loop control variable.
The test condition is checked to decide whether another pass should be made. The body is the statement or block that is repeated. The update changes the loop control variable so that the condition eventually turns false.
Leave out the update, or write one that moves in the wrong direction, and the condition never becomes false: the loop runs for ever. Write a condition that is false at the start, and the body never runs at all. Almost every faulty loop you write will be a fault in one of these four parts, so when a loop misbehaves, name the four parts and check them one at a time.
6.6.2 The for Loop
The for loop gathers three of the four parts into a single line:
for (initialisation; test condition; update)
statementThe initialisation runs once, before anything else. The condition is then tested; if it is true the body runs, then the update runs, and the condition is tested again. If it is false the loop ends at once.
for (i = 1; i <= 5; i++)
System.out.println(i);Because all the bookkeeping sits in one place, a reader can see at a glance how many passes are intended. This is why the for loop is the natural choice when the number of repetitions is known before the loop starts — a fixed count, the number of students in a class, the number of terms in a series.
The loop control variable may be declared in the header itself:
for (int i = 1; i <= 5; i++)Its scope is then the loop alone, exactly as section 6.2 described, and the name is unavailable after the closing brace. That is usually what you want: a counter has no business outliving the loop that needed it.
| Pass | i at the test |
Condition i <= 4 |
marks read |
total after the pass |
|---|---|---|---|---|
| 1 | 1 | true | 62 | 62 |
| 2 | 2 | true | 78 | 140 |
| 3 | 3 | true | 45 | 185 |
| 4 | 4 | true | 90 | 275 |
| — | 5 | false | — | 275 |
Result: the loop ends with total equal to 275, and the average is printed as 68.75. The trace shows the two things worth noticing about any counted loop: the condition is tested one more time than the body runs, and the loop control variable finishes one step past the last value it used.
6.6.3 The while Loop
Often the number of passes is not known in advance. How many digits has this number? How many entries come before the end of the data? For these the while loop is the right shape:
while (condition)
statementThere is no room in the header for initialisation or update, so both become your responsibility: the loop control variable is given its starting value before the loop, and changed inside the body. Forget the change and the loop never ends.
The condition is tested before every pass, including the first. If it is false at the outset the body does not run even once — and that is a feature, not a defect. A loop that must cope with “there is nothing to do” should be written this way.
| Pass | number at the test |
Condition number > 0 |
digit |
reversed after the pass |
number after the pass |
|---|---|---|---|---|---|
| 1 | 4172 | true | 2 | 2 | 417 |
| 2 | 417 | true | 7 | 27 | 41 |
| 3 | 41 | true | 1 | 271 | 4 |
| 4 | 4 | true | 4 | 2714 | 0 |
| — | 0 | false | — | 2714 | 0 |
Result: the loop ends with reversed equal to 2714. The trace also shows why the loop is guaranteed to stop: number is strictly smaller after every pass, and no sequence of whole numbers can go on decreasing for ever.
6.6.4 The do while Loop
The do while loop puts the test at the bottom:
do
{
statement
}
while (condition);The body runs first and the condition is tested afterwards. If it is true, control returns to the top of the body; if it is false, the loop ends. The body therefore runs at least once, whatever the condition says.
That is the whole reason the construct exists. Some work must be done before there is anything to test. Asking a user for a valid mark is the standard case: you cannot judge the input until you have read it.
int marks;
do
{
System.out.print("Enter marks out of 100 : ");
marks = in.nextInt();
}
while (marks < 0 || marks > 100);The prompt appears once for a correct value, and again for every incorrect one. Written as a while loop, the same task needs either a duplicated read before the loop or an artificial starting value chosen only to make the condition true the first time.
6.6.5 Entry Controlled and Exit Controlled Loops
The three statements fall into two families according to where the condition is tested.
An entry controlled loop tests the condition before each pass, at the point of entry. The for and while loops are entry controlled, and either may run its body zero times.
Diagram
Fig. 6.6-2 – flow diagram of an entry controlled loop; an arrow enters a diamond labelled ‘condition true?’ from above; the ‘True’ branch leads down into a box labelled ‘body’; an arrow from that box returns upwards to the diamond; the ‘False’ branch leaves the diamond to the right and continues downwards, labelled ‘exit’
An exit controlled loop tests the condition after each pass, at the point of exit. The do while loop is exit controlled, and its body always runs at least once.
Diagram
Fig. 6.6-3 – flow diagram of an exit controlled loop; an arrow enters a box labelled ‘body’ from above; an arrow from that box leads down into a diamond labelled ‘condition true?’; the ‘True’ branch returns upwards to the box; the ‘False’ branch leaves to the right and continues downwards, labelled ‘exit’
| Feature | for |
while |
do while |
|---|---|---|---|
| Where the condition is tested | Before each pass | Before each pass | After each pass |
| Family | Entry controlled | Entry controlled | Exit controlled |
| Least possible number of passes | 0 | 0 | 1 |
| Where the initialisation is written | In the header | Before the loop | Before the loop |
| Where the update is written | In the header | Inside the body | Inside the body |
| Semicolon after the closing line | No | No | Yes |
| Suits | A known number of passes | An unknown number of passes | Work that must be done before it can be judged |
6.6.6 Infinite Loops and Empty Loops
An infinite loop is a loop whose condition never becomes false. It is usually a mistake, and almost always the same mistake — a missing update:
i = 1;
while (i <= 10)
System.out.println(i);Nothing in the body changes i, so the condition stays true and the line is printed until the program is stopped by hand. A loop that never ends does not wait quietly; it occupies the processor for as long as it runs. Checking that some statement inside the body can make the condition false is therefore part of writing the loop, not part of testing it afterwards.
An infinite loop can also be written deliberately, by leaving the parts out altogether:
for ( ; ; )This is legal — a missing test condition in a for header is taken to be true — and it is used when the exit is decided inside the body rather than at the top. Section 6.7 shows how break provides that exit. A deliberate infinite loop with no break anywhere inside it is simply a bug with confident punctuation.
An empty loop is one whose body does no work, written as a bare semicolon or an empty pair of braces:
for (i = 1; i <= 1000; i++);The loop counts to a thousand and does nothing else. Loops like this were once used to create a short pause, but they are not a dependable way of doing so: how long they take depends entirely on the machine, and a modern runtime may notice that the loop has no effect and remove it.
6.7 Jump Statements inside Loops
A loop, left alone, ends in exactly one way: the condition is tested and found false. That is a clean rule, and most of the time it is all you need.
Sometimes it is wasteful. If you are searching through a list and find what you want on the second entry, testing the remaining nine hundred is pointless. Sometimes it is awkward: one particular value in the middle of a range may need to be passed over without disturbing the rest of the loop.
Java provides two jump statements for these situations. A jump statement computes nothing; it moves control to a different point in a strictly limited way.
6.7.1 The break Statement
You have already met break in the switch case statement, where it ends the switch block. Inside a loop it does the corresponding thing: it ends the loop at once.
When a break runs, the rest of the body is abandoned, the condition is not tested again, and control passes to the first statement after the loop. Fig. 6.7-1 shows the exit it creates.
Diagram
Fig. 6.7-1 – flow diagram showing break; at the top a diamond labelled ‘loop condition true?’ whose ‘True’ branch enters a box labelled ‘body of the loop’; inside the body a small diamond labelled ‘break condition?’ whose ‘Yes’ branch leaves the loop by a straight arrow to a box below labelled ‘statement after the loop’, and whose ‘No’ branch continues through the rest of the body and returns upward to the loop condition; the ‘False’ branch of the loop condition also reaches the box labelled ‘statement after the loop’
Testing whether a number is prime shows why this is worth having. A number is prime when no whole number between 2 and half of it divides it exactly. The moment one divisor is found the answer is settled, and every further division is wasted work.
| Pass | i |
number % i |
Divisor found? | Action |
|---|---|---|---|---|
| 1 | 2 | 1 | no | go on to the next pass |
| 2 | 3 | 1 | no | go on to the next pass |
| 3 | 4 | 3 | no | go on to the next pass |
| 4 | 5 | 1 | no | go on to the next pass |
| 5 | 6 | 1 | no | go on to the next pass |
| 6 | 7 | 0 | yes | set prime to false and leave the loop |
Result: the loop ends after six passes instead of the forty-four it would otherwise have made, and 91 is reported as not prime. The trace shows what break buys — and also what it does not: had the input been 89, no divisor would have been found and every one of the passes would still have run.
6.7.2 The continue Statement
The continue statement does not end the loop. It abandons the rest of the current pass and moves on to the next one, as Fig. 6.7-2 shows.
Diagram
Fig. 6.7-2 – flow diagram showing continue; a diamond labelled ‘loop condition true?’ whose ‘True’ branch enters a box labelled ‘first part of the body’; below that a diamond labelled ‘skip condition?’ whose ‘Yes’ branch curves back to the loop condition without passing through the remaining body, the curving arrow labelled ‘continue’, and whose ‘No’ branch enters a box labelled ‘rest of the body’ that then returns to the loop condition; the ‘False’ branch of the loop condition leaves downwards, labelled ‘exit’
Printing the numbers from 1 to 10 while passing over the multiples of 3 is the plainest illustration:
for (i = 1; i <= 10; i++)
{
if (i % 3 == 0)
continue;
System.out.print(i + " ");
}The output is:
1 2 4 5 7 8 10
Notice what did not happen. The loop was not cut short — all ten passes were made. On the passes where i held 3, 6 or 9 the printing was skipped, and control went straight to the update.
That last detail is where continue becomes dangerous. In a for loop the update lives in the header, so it runs even when the body is abandoned. In a while loop the update is an ordinary statement inside the body, and a continue written above it jumps clean past it.
| Feature | break |
continue |
|---|---|---|
| Effect on the loop | Ends it at once | Ends the current pass only |
| Where control goes next | The statement after the loop | The update, then the condition |
| Is the condition tested again? | No | Yes, before the next pass |
| Passes still to come | None | Possibly many |
Usable in a switch as well |
Yes, to end the switch block | No |
| Typical use | Stopping a search once the answer is known | Passing over values that need no processing |
6.8 Nested Loops
The body of a loop is a block, and a block may contain any statement at all — including another loop. A nested loop is a loop written inside the body of another loop.
The idea is needed whenever the data has two dimensions to it. For every row of a seating plan, every seat in that row. For every day of the timetable, every period of that day. For every student in a class, every subject they offer. The outer loop steps through the first list; the inner loop runs right through the second list before the outer one moves on.
6.8.1 How Nesting Works
The rule that governs every nested loop is short: the inner loop completes all of its passes for each single pass of the outer loop.
Diagram
Fig. 6.8-1 – structure of a nested loop; a large rectangle labelled ‘outer loop’ containing, from top to bottom, a box labelled ‘statements before the inner loop’, a smaller rectangle labelled ‘inner loop’ containing a box labelled ‘body of the inner loop’ with a curved arrow returning to the top of the smaller rectangle, and below it a box labelled ‘statements after the inner loop’; a curved arrow returns from the bottom of the large rectangle to its top; label the inner arrow ‘repeats fully every time’ and the outer arrow ‘one pass of the outer loop’
Two consequences follow, and both matter.
The first is arithmetic. If the outer loop makes 5 passes and the inner loop makes 8 passes each time, the innermost statements run 40 times. Nesting multiplies work, and it multiplies it quickly: two nested loops over the same 1000 items give a million passes.
The second is about the inner loop control variable. It must be given its starting value again at the beginning of every outer pass, because by the end of the previous pass it has been driven past the finishing line. When the inner loop is a for loop this happens automatically, since its initialisation sits in its own header and that header is reached afresh each time. When the inner loop is a while loop, the reset is your responsibility.
Nesting is not limited to two levels, and the kinds may be mixed: a while inside a for, or a do while inside a while. What never changes is that the inner construct must be complete before the outer one continues — the loops nest one inside the other, and they may not overlap.
6.8.2 Nested for Loops
Two counted loops, one inside the other, are the commonest nesting of all. The outer counter usually decides which line is being produced, and the inner counter decides what goes on that line.
Outer pass (i) |
Values taken by j |
Inner passes | Printed on that line |
|---|---|---|---|
| 1 | 1 | 1 | 1 then a new line |
| 2 | 1, 2 | 2 | 1 2 then a new line |
| 3 | 1, 2, 3 | 3 | 1 2 3 then a new line |
Result: three lines are printed and the inner body runs six times in all, which is \(3 \times 4 / 2\). The trace shows that j starts again at 1 on every outer pass — the for header takes care of it — while i moves on only when the inner loop has finished completely.
6.8.3 Nested while and do while Loops
The same structure can be built from while loops, and the logic is identical. What changes is the bookkeeping: nothing is done for you.
i = 1;
while (i <= rows)
{
j = 1;
while (j <= i)
{
System.out.print(j + " ");
j++;
}
System.out.println();
i++;
}Look at where j = 1; sits. It is inside the outer body, above the inner loop, so it runs once per line. That placement is the whole difference between a working nested loop and the commonest fault in this section.
A do while loop can be nested in exactly the same way, with the same requirement to reset the inner variable — and one extra caution. Because an exit controlled inner loop always makes at least one pass, a nested do while will produce output even for an outer pass on which nothing was wanted. If a line of the triangle should sometimes be empty, an entry controlled inner loop is the correct choice.
6.9 Converting One Loop into Another
The three looping statements do not differ in what they can compute. Anything written with a for loop can be written with a while loop, and almost anything written with a while loop can be written with a do while loop. Converting between them is therefore not a matter of rethinking the problem. It is a matter of taking the four parts named in section 6.6.1 — initialisation, test condition, body and update — and putting each one where the new construct expects to find it.
That is the whole method, and it is worth stating as a procedure before you try it on anything complicated.
From for to while
Take the three items out of the for header and place them in the order the while loop needs: the initialisation above the loop, the condition in the header, the update as the last statement of the body.
Here is a loop that displays the multiples of 7 up to 70, written first with for:
for (i = 7; i <= 70; i = i + 7)
System.out.print(i + " ");and then with while:
i = 7;
while (i <= 70)
{
System.out.print(i + " ");
i = i + 7;
}Both print the same line:
7 14 21 28 35 42 49 56 63 70
Two details in that conversion are not optional. The body needs braces, because it now holds two statements rather than one. And the update must be the last statement of the body, not the first.
From while to for
The reverse conversion gathers the parts instead of spreading them: the statement above the loop becomes the initialisation, the header condition stays where it is, and the last statement of the body becomes the update.
Any of the three parts of a for header may be left empty when there is nothing natural to put there. A while loop that reads until the user types a stopping value has no counter at all, and forcing it into a for header produces for ( ; number != 0 ; ), which is legal, correct and considerably harder to read than the while loop it came from. Convert in this direction only when there really is a loop control variable to gather up.
From an entry controlled loop to do while
This conversion is the one that needs care, because the two constructs are not equivalent. A for or while loop may make no passes at all; a do while loop always makes at least one. Moving the condition to the bottom is safe only when the body is certain to be wanted once.
When it is not certain, the loop must be guarded by an if that repeats the condition:
i = 7;
if (i <= 70)
{
do
{
System.out.print(i + " ");
i = i + 7;
}
while (i <= 70);
}The guard restores the behaviour that was lost. To see what it is protecting against, change the limit from 70 to 5. The for and while versions print nothing, which is right, and the guarded version prints nothing too — but an unguarded do while prints 7, a value that lies outside the range it was asked for.
From do while to an entry controlled loop
Going the other way, the first pass has to be accounted for. Either the body is written once before an ordinary while loop, which duplicates it, or the condition is restated so that it is true on entry. A validation loop of the kind in section 6.6.4 is usually converted by giving the variable a starting value that is deliberately invalid, so that the condition holds the first time — a workable trick, and a clear sign that the do while was the honest construct for that task.
One caution about continue
A for loop and its while equivalent stop behaving alike the moment a continue appears in the body. In the for loop the update sits in the header and runs anyway; in the while loop it is an ordinary statement that the continue jumps over, which is exactly the fault described in section 6.7.2. If the loop you are converting contains a continue, the update cannot simply be moved to the bottom of the body — it must be placed where every path through the body will reach it.
6.10 Summary
Statements and blocks
A statement is one complete instruction, built from tokens. A simple statement ends with a semicolon; whitespace and indentation are for the reader, not for the compiler.
Executable statements act when the program runs — input and output, assignment, conditional and looping statements. Non-executable statements do not: a declaration informs the compiler, and a comment informs the reader.
A declaration introduces a name and fixes its type, and must appear before the name is used. A local variable has no automatic starting value, so reading one before it has been assigned is a compile-time error.
An assignment evaluates the expression on the right and stores the result in the variable on the left. This is why
count = count + 1is sensible as an instruction though absurd as an equation: the old value is used to compute the new one.Java has three forms of comment — the single line comment, the multiline comment, and the documentation comment, which the
javadoctool of the JDK reads.A block is a group of statements in braces, treated as a single statement. Every conditional and looping statement governs exactly one statement, so braces are what allow several statements to be governed together.
Scope, lifetime and the flow of control
The scope of a local variable runs from its declaration to the closing brace of its block. An inner block can use names declared outside it; an outer block cannot use names declared within an inner one. Java does not allow a local variable to be hidden by another local variable of the same name.
Scope is a region of program text, fixed at compile time; lifetime is a period during execution. A variable declared inside a repeated block is created and destroyed on every pass, which is why a running total must be declared outside the loop that builds it.
Control passes through statements in written order unless a control statement redirects it. Selection decides whether statements run; iteration decides how many times they run. Every condition in Java must be of type
boolean, so an assignment written where a comparison was meant fails to compile rather than succeeding silently.
Conditional statements
ifruns its statement or skips it.if elseruns exactly one of two alternatives — never both and never neither.An
if else ifladder tests its conditions strictly in order and stops at the first true one, so the order of the rungs is part of the logic. Independentifstatements are all tested, and where their conditions overlap the last assignment wins.A nested
ifasks a second question only after the first has been answered. Anelsepairs with the nearest unmatchedif, whatever the indentation suggests; braces are what prevent a dangling else.The ternary operator is an expression, not a statement. It yields one of two values and belongs where both branches supply a value for the same thing.
The switch case statement
A
switchmatches one expression for equality against constant case labels. It acceptsbyte,short,char,int, their wrapper classes and enumerated types, andStringfrom Java 7 onwards; it accepts neither ranges nor conditions as labels.Case labels are entry points, not compartments. Control enters at the matching label and continues through the labels below it until a
breakstops it — that continuation is fall through, useful when stacked labels share one action and a bug when abreakhas simply been forgotten.The
defaultcase catches every value that matches no label, as the finalelsedoes in a ladder. Fixed values suit aswitch; ranges and multi-variable tests suit a ladder.
Looping statements
Every loop has four parts: initialisation, test condition, body and update. A missing or wrong update gives an infinite loop; a condition false at the start gives no passes at all.
The
forloop carries three of the four parts in its header and suits a known number of passes. Thewhileloop tests before each pass and suits an unknown number. Thedo whileloop tests after each pass, so its body always runs at least once.forandwhileare entry controlled and may make zero passes;do whileis exit controlled and makes at least one. The closingwhileof ado whileloop takes a semicolon.An infinite loop occupies the processor until the program is stopped, so every loop must contain something that can make its condition false. An empty loop, whose body is an empty statement, does nothing useful and is not a dependable way to create a pause; a stray semicolon after a loop header creates one by accident.
Reading and adding the marks of a class costs \(O(n)\), where \(n\) is the number of students. Reversing the digits of a number costs \(O(d)\), where \(d\) is the number of digits.
Jump statements and nested loops
breakends the innermost loop orswitchcontaining it and passes control to the statement after it.continueabandons only the current pass and returns to the top of the loop.In a
forloop the update runs even after acontinue, because it sits in the header. In awhileloop acontinuewritten above the update skips it, and the loop never ends.Testing a number for primeness by trial division costs \(O(n)\) in the worst case, where \(n\) is the number tested. A
breakon the first divisor found improves the typical case but not the worst case, since a prime number has no divisor to find.In a nested loop the inner loop completes all its passes for every single pass of the outer loop, so the work multiplies. The inner loop control variable must be reset at the start of each outer pass — automatic in a
forheader, and your responsibility in awhileloop.Printing a triangle whose line numbered i holds i values costs \(n(n + 1)/2\) passes for \(n\) lines; the dominant term is the one in \(n^2\), so the cost is \(O(n^2)\).
The three looping statements are interchangeable in what they compute, and converting between them means relocating the four parts. Converting an entry controlled loop to a
do whileloop needs a guardingif, because ado whileloop cannot make zero passes.
6.11 Key Terms
assignment statement — a statement that evaluates the expression on its right and stores the result in the variable named on its left.
block — zero or more statements enclosed in braces, treated by the language as a single statement.
break statement — a jump statement that ends the innermost loop or
switchcontaining it, passing control to the statement after it.case label — a constant value written in a
switchstatement, marking the point at which control enters when the switch expression equals that value.comment — text written for a human reader and removed by the compiler, so that it can never affect what the program does.
compound assignment operator — an operator such as
+=or*=that combines an arithmetic operation with an assignment to the same variable.condition — an expression of type
boolean, whose value decides what a conditional or looping statement does next.conditional statement — a control statement that tests a condition and selects which of the statements under its control are executed.
control statement — a statement whose purpose is to decide which statements run next, rather than to compute a value.
dangling else — the mis-attachment that occurs when an
elsepairs with a nearerifthan the writer intended, since it always pairs with the nearestifthat has none.declaration statement — a statement that introduces a name into the program and fixes the type of value it may hold.
default case — the label in a
switchstatement to which control passes when the switch expression matches no case label.do while loop — a loop written with the
do whilestatement, in which the condition is tested after each pass, so the body always runs at least once.documentation comment — a comment beginning with
/**, written above a class or method and read by thejavadoctool to generate documentation.empty loop — a loop whose body is an empty statement or an empty block, and which therefore repeats without doing any work.
empty statement — a statement consisting of a semicolon alone, which does nothing.
entry controlled loop — a loop whose condition is tested before each pass, so that it may make no passes at all; the
forandwhileloops are of this kind.executable statement — a statement that produces machine code and performs an action when the program runs.
exit controlled loop — a loop whose condition is tested after each pass, so that it always makes at least one; the
do whileloop is of this kind.fall through — the continuation of control from one case of a
switchstatement into the case below it, when nobreakstops it.flow of control — the order in which the statements of a program are carried out while it runs.
for loop — a loop written with the
forstatement, which carries the initialisation, the test condition and the update in a single header.if else if ladder — a chain of conditionals in which each
elsepart holds the nextif, the conditions being tested in order until one is found true.infinite loop — a loop whose condition never becomes false, so that it continues until the program is stopped.
iteration — the repeated execution of a group of statements while a condition remains true.
JDK — Java Development Kit, the set of tools with which Java programs are compiled, run and documented.
lifetime — the period during execution for which a variable’s storage exists; for a local variable, from its declaration until control leaves its block.
local variable — a variable declared inside a method or a block, and available only within it.
loop — a control statement in which a statement or block is executed repeatedly while a stated condition is true.
loop control variable — the variable whose value is set at the start of a loop, tested by its condition and changed by its update.
multiline comment — a comment beginning with
/*and ending with*/, which may extend over several lines.nested if statement — an
ifstatement written inside theifpart or theelsepart of anotherifstatement.nested loop — a loop written inside the body of another loop, the inner one completing all its passes for each single pass of the outer one.
non-executable statement — a statement that performs no action when the program runs, such as a declaration or a comment.
scope — the region of the program text within which a name may be used.
selection — the choosing of one group of statements to execute in place of another, according to a condition.
single line comment — a comment beginning with
//and continuing to the end of that line.statement — one complete instruction in a program, formed from valid tokens and, if simple, ended by a semicolon.
switch case statement — a control statement that matches one expression for equality against constant case labels and jumps to the label that matches.
ternary operator — the operator written
? :, which takes three operands and yields one of two values according to a condition.token — the smallest meaningful piece of a program: a keyword, a name, a literal, an operator or a punctuation mark.
visibility — which names a given statement can reach, namely those in scope in its own block and in the blocks enclosing it.
while loop — a loop written with the
whilestatement, in which the condition is tested before each pass and the initialisation and update are written by the programmer outside the header.
6.12 Points to Ponder
Sequence, selection and iteration are enough to express any computation, and the three looping statements of Java can all do one another’s work. Why would a language designer add constructs that give the language no new power at all?
In a
switchstatement, control falls through to the next case unless abreakstops it — and a forgottenbreakis one of the commonest bugs in the language. What might be said in favour of that choice, and what would be lost if the language stopped at the end of each case automatically?The compiler refuses to let you read a local variable that has not been assigned, yet it will happily accept a loop that never ends. Why can it be certain about one and not the other, and what does that tell you about the kinds of error a compiler can be expected to catch?
Ending the search for a divisor with
breakmakes the prime test much faster for most numbers, but leaves the worst case exactly as slow as before. When is an improvement that never shows in the worst case still worth making, and when is it not?Java will not allow a local variable to be hidden by another local variable of the same name, although some other languages allow exactly that. What does the rule protect the programmer from, and what does it cost in return?
Almost every fault in this chapter — the missing braces, the stray semicolon, the dangling
else, the forgottenbreak— produces a program that compiles and runs while doing the wrong thing. Is a language that accepts such programs poorly designed, or is there something these four mistakes have in common that no language could reasonably prevent?
6.13 Objective Questions
- Which of the following is an exit controlled loop?
forwhiledo while- None of these [1 mark]
- The least number of times the body of a
whileloop can be executed is:
- 0
- 1
- 2
- It depends on the update [1 mark]
- Which of the following types may not be used as the expression of a
switchstatement?
intchardoubleString[1 mark]
- When a
continuestatement is executed inside aforloop, control passes next to:
- The statement after the loop
- The update in the loop header
- The first statement of the body
- The condition, without the update [1 mark]
- A variable declared inside the body of a loop goes out of scope at:
- The end of the method
- The closing brace of the loop body
- The last pass of the loop
- The end of the class [1 mark]
- Assertion (A): A
do whileloop executes its body at least once, whatever its condition. Reason (R): The condition of ado whileloop is tested only after the body has been executed. Choose the correct option:
- Both Assertion (A) and Reason (R) are true and Reason (R) is the correct explanation of Assertion (A).
- Both Assertion (A) and Reason (R) are true and Reason (R) is not the correct explanation of Assertion (A).
- Assertion (A) is true but Reason (R) is false.
- Assertion (A) is false but Reason (R) is true. [1 mark]
- Match each item in Column A with its description in Column B.
| Column A | Column B |
|---|---|
a. break |
i. Entered when the switch expression matches no case label |
b. continue |
ii. Ends the innermost loop or switch containing it |
c. default |
iii. Executed when the condition of its if is false |
d. else |
iv. Abandons the current pass and returns to the top of the loop |
[1 mark]
Fill in the blank: a group of statements enclosed in braces and treated by the language as a single statement is called a ________. [1 mark]
State whether the following is true or false: a case label in a
switchstatement may be a variable. [1 mark]Give the full form of JDK. [1 mark]
6.14 Check Your Concepts
Define a block. State one reason why braces are needed when more than one statement is to be governed by an
if. [2 marks]Define the scope of a variable. Give one example of a variable whose scope is a single loop. [2 marks]
What is meant by fall through in a
switch casestatement? State one situation in which it is deliberately used. [2 marks]Differentiate between an entry controlled loop and an exit controlled loop, naming one Java statement of each kind. [3 marks]
Differentiate between the
breakstatement and thecontinuestatement as used inside a loop. [3 marks]Rewrite the following loop using a
whileloop, without changing what it prints.
for (int k = 20; k >= 5; k = k - 3)
System.out.print(k + " ");[3 marks]
A shop allows a discount of 5% on bills up to ₹1000, 10% on bills above ₹1000 and up to ₹5000, and 15% on bills above ₹5000. Explain, with reference to this example, why the order of the conditions in an
if else ifladder affects the result, and state what would happen if the three tests were written as three separateifstatements instead. [4 marks]A loop repeatedly divides a positive integer by 2, discarding any fraction, and counts the divisions until the value reaches 1. State the complexity of this method in big O notation, say clearly what n counts, and justify your answer. [4 marks]
Compare the
switch casestatement with theif else ifladder under four heads of difference. Then state, with a reason in each case, which of the two you would use for (i) awarding a grade from a mark out of 100, and (ii) displaying the name of a weekday from its number. [6 marks]
6.15 Output & Debugging
- Predict the output of the following segment, and state the value of
iafter the loop has ended.
int i, sum = 0;
for (i = 1; i <= 6; i++)
{
if (i % 2 == 0)
continue;
sum = sum + i;
}
System.out.println(sum + " " + i);[2 marks]
- Write the output of the following segment and state how many lines are printed.
int n = 3;
switch (n)
{
case 2:
System.out.println("two");
case 3:
System.out.println("three");
case 4:
System.out.println("four");
break;
default:
System.out.println("other");
}[2 marks]
- The following segment is intended to add the numbers from 1 to 5. It does not compile. Identify the single fault, explain the message the compiler gives, and rewrite the segment correctly.
int count = 0;
for (int j = 1; j <= 5; j++);
{
count = count + j;
}
System.out.println(count);[3 marks]
- The following segment is intended to print the numbers from 1 to 5, omitting 3. State what it actually prints, explain why it does not end, and correct it.
int i = 1;
while (i <= 5)
{
if (i == 3)
continue;
System.out.println(i);
i++;
}[3 marks]
- Show a dry run of the following segment in the form of a table, give its output, and state how many times the body of the inner loop is executed.
int x, y;
for (x = 1; x <= 3; x++)
{
for (y = x; y <= 3; y++)
System.out.print(x * y + " ");
System.out.println();
}[4 marks]
6.16 Programming Practice
Write a program that reads a positive integer from the user and displays the sum of its digits. For the input 4085 the program should display the sum of 4, 0, 8 and 5. Submit the algorithm, the complete class and a variable description table. [5 marks]
Write a menu driven program using a
switch casestatement that offers the user two choices: choice 1 displays the multiplication table of a number entered by the user, up to ten terms; choice 2 displays the sum of the first n natural numbers for an n entered by the user. Any other choice should display a suitable message. Submit the complete class. [6 marks]Write a program that reads a number n between 1 and 9 and prints n lines using nested loops, such that the first line contains n copies of n, the second line n − 1 copies of n − 1, and so on, down to a last line containing a single 1. For n = 4 the first line is
4 4 4 4and the last line is1. Submit the complete class and a variable description table. [5 marks]A teacher enters the marks of a class one after another. Write a program that reads the number of students and then their marks, ignores any mark below 0 or above 100 by means of a
continuestatement, and finally displays the number of valid marks entered and how many of them are 40 or above. Submit the algorithm and the complete class, and state one input with which you would test the handling of an invalid mark. [6 marks]
6.1.5 Comments
A comment is text written for a human reader. The compiler removes comments before it does anything else, so a comment can never change what a program does — which is exactly why it can never be trusted to be correct unless you keep it correct.
Java offers three forms.
A single line comment begins with
//and runs to the end of that line. It is the right choice for a short note beside a statement.A multiline comment begins with
/*and ends with*/, and may run across as many lines as you need. It is used for a longer explanation, such as the description of a method’s method of solution.A documentation comment begins with
/**and ends with*/. It is written immediately above a class or a method, and it is the form that thejavadoctool of the JDK reads to generate documentation pages automatically. If you never run that tool, a documentation comment behaves exactly like a multiline comment.Every kind of statement in one program
The program below is deliberately small. Its purpose is not to compute anything difficult, but to let you see a declaration, an assignment, an output statement and all three forms of comment sitting together in a single class.
fine payable on a book returned late to the school library Problem: a school library charges a fixed rate per day on a book returned late. For a book that is six days late at a rate of ₹1.50 per day, display the number of days and the fine payable. The two input values are fixed inside the program.
Sample output:
Variable description:
[Table:] Table 6.2 – variable description for the class LibraryFine [/Table]
daysLateintratePerDaydoublefinedoubleNotes: the fine is declared as
doublerather thanintbecause the rate is not a whole number; hadfinebeen anint, the fractional part would have been lost. The printed value appears as9.0and not as9, because adoublealways carries a fractional part even when that part is zero. Notice also that the three declarations sit at the top, before any name is used — the order is not a style rule here but a requirement.Key in the program above, change the rate to 2.25 and the number of days to 4, and predict the printed fine before you run it. Then change the type of
finefromdoubletointand record the compiler’s response.