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’

NoteCuriosity Corner

Q1. What changes if you leave out the braces around two statements that are meant to run together after an if? Q2. A while loop and a do while loop look almost identical. Is there any input for which they behave differently? Q3. What actually happens in a switch case statement if you forget one break? Q4. Both break and continue interrupt a loop. Do they hand control back to the same place? Q5. Why does a variable declared inside a loop vanish the moment the loop ends?

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.

NoteDefinition

A statement is one complete instruction to the computer, formed from valid tokens of the language. A simple statement is terminated by a semicolon; a group of statements enclosed in braces forms a single compound statement.

Q. If whitespace is ignored, why does every book insist on indenting the body of a loop?

A. Because the compiler is not the only reader. Indentation shows which statements belong together, and a program whose layout disagrees with its braces is the single easiest way to introduce a bug you cannot see.

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.

Table 6.1 – kinds of statement in Java, with an example of each and whether it does anything when the program runs

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.

using a local variable before it has been given a value What students write:

int total;
System.out.println(total);

What happens: the program does not run at all. The compiler reports that the variable total might not have been initialized, and refuses to produce a class file. It does not quietly assume zero.

The fix: give the variable a value before you read it, either in the declaration itself or in an assignment that is certain to run.

int total = 0;
System.out.println(total);

A local variable — one declared inside a method — has no automatic starting value, so the compiler insists that you supply one.

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.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 the javadoc tool 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.

/**
 * Calculates the fine payable on a late library book.
 * Inputs are fixed inside the program.
 */
class LibraryFine
{
    public static void main(String args[])
    {
        /* Three declarations: each name is introduced
           and its type is fixed. */
        int daysLate;
        double ratePerDay;
        double fine;

        daysLate = 6;                    // assignment of a fixed value
        ratePerDay = 1.50;               // assignment of a fixed value
        fine = daysLate * ratePerDay;    // assignment of a computed value

        System.out.println("Days late    : " + daysLate);
        System.out.println("Fine payable : " + fine);
    }
}

Sample output:

Days late    : 6
Fine payable : 9.0

Variable description:

[Table:] Table 6.2 – variable description for the class LibraryFine [/Table]

Variable Type Purpose
daysLate int Number of days by which the book is overdue
ratePerDay double Fine charged for each late day
fine double Total fine payable, computed as days × rate

Notes: the fine is declared as double rather than int because the rate is not a whole number; had fine been an int, the fractional part would have been lost. The printed value appears as 9.0 and not as 9, because a double always 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 fine from double to int and record the compiler’s response.

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.

NoteDefinition

A block is zero or more statements enclosed in braces. It may appear anywhere a single statement may appear, and it is treated as one statement.

Q. Does the closing brace of a block need a semicolon after it?

A. No. The brace already marks the end of the group. A semicolon written there is an empty statement — a statement that does nothing — and after a block it is harmless. Written in the wrong place, the same stray semicolon causes bugs that are very hard to see, as section 6.6 will show.

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.

applying a discount to a bill, with the discount confined to a block Problem: a shop’s bill comes to ₹250 before a fixed discount of ₹25. Display the discount and then the amount payable, keeping the discount available only while it is being applied. Both values are fixed inside the program.

class BillDiscount
{
    public static void main(String args[])
    {
        int total = 250;

        {
            int discount = 25;
            total = total - discount;
            System.out.println("Discount applied : " + discount);
        }

        System.out.println("Amount payable   : " + total);
    }
}

Sample output:

Discount applied : 25
Amount payable   : 225

Variable description:

[Table:] Table 6.3 – variable description for the class BillDiscount [/Table]

Variable Type Purpose
total int Bill amount, reduced to the amount payable
discount int Amount deducted, needed only inside the inner block

Notes: total is declared in the method body, so it is still available after the inner block closes — which is why the last line can print it. discount is declared inside the block, so adding System.out.println(discount); after the closing brace would stop the program from compiling. A bare block like this is legal but uncommon; in real programs a block is nearly always attached to a conditional or a looping statement, and the scope rule is exactly the same there.

declaring the same variable name twice in the same method What students write:

int count = 0;
{
    int count = 5;
}

What happens: the program does not compile. The compiler reports that the variable count is already defined in the method, and stops. Java does not allow a local variable to be hidden by another local variable of the same name.

The fix: choose a different name for the inner variable, or reuse the outer one if that is what you meant.

int count = 0;
{
    count = 5;
}

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.

Table 6.4 – scope compared with lifetime

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.

Q5. Why does a variable declared inside a loop vanish the moment the loop ends?

A. because the body of a loop is a block, and a variable declared inside a block is local to it. Its storage is created when the declaration is executed and released when control leaves the block — which happens at the end of every single pass, not just at the end of the loop. The name is also out of scope after the closing brace, so the compiler will not even allow you to mention it there. If you need the value after the loop, the variable must be declared before the loop begins.

Write a short class in which a variable is declared inside an inner block and printed after that block has closed. Compile it, copy down the exact message the compiler gives, then move the declaration outside the block and confirm that the program now runs.

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.

Table 6.5 – selection compared with iteration

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.

Q. What happens if you write if (marks = 40) instead of if (marks == 40)?

A. The program does not compile. The single = makes it an assignment, whose value is the int 40, and Java requires a boolean there. In a language that accepts numbers as conditions the same slip compiles and quietly succeeds every time. Here the compiler catches it for you.

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.

NoteDefinition

A conditional statement evaluates a condition of type boolean and, according to whether it is true or false, selects which of the statements under its control are executed.

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)
    statement

If 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.

Q1. What changes if you leave out the braces around two statements that are meant to run together after an if?

A. only the first of them stays under the condition. An if governs exactly one statement, so without braces the second statement is not part of the conditional at all — it simply follows the if and runs every time, whatever the condition. The program still compiles, and the indentation still suggests that both lines belong together, which is why the error is so easy to miss. Braces turn the two statements into a single block, and the if then governs the block.

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
    statement2

Exactly 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
    statement4

The 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.

monthly bus fare from a distance slab Input: the distance travelled, in whole kilometres, as a positive integer. Output: the monthly fare in rupees, according to the slabs 5 km or less → 400; above 5 up to 10 → 600; above 10 up to 20 → 850; above 20 → 1100. Method:

1. Read the distance.
2. If distance <= 5, set fare to 400.
3. Otherwise, if distance <= 10, set fare to 600.
4. Otherwise, if distance <= 20, set fare to 850.
5. Otherwise, set fare to 1100.
6. Display the fare.

Why it works: the slabs are tested in increasing order of distance, so a test is reached only when every lower slab has already failed. Each test therefore needs to state its upper limit alone; the lower limit is guaranteed by the tests above it. Cost: at most four comparisons, whatever the distance. The work does not grow with the size of the input at all, so the cost is \(O(1)\).

monthly school bus fare from a distance slab Problem: read the distance in whole kilometres from the user and display the monthly bus fare, using the slabs listed above.

import java.util.Scanner;

class BusFare
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int distance;
        int fare;

        System.out.print("Enter the distance in km : ");
        distance = in.nextInt();

        if (distance <= 5)
            fare = 400;
        else if (distance <= 10)
            fare = 600;
        else if (distance <= 20)
            fare = 850;
        else
            fare = 1100;

        System.out.println("Monthly fare : " + fare);
    }
}

Sample output, for the value 12 typed by the user:

Enter the distance in km : 12
Monthly fare : 850

Variable description:

[Table:] Table 6.6 – variable description for the class BusFare [/Table]

Variable Type Purpose
in Scanner Reads the distance typed by the user
distance int Distance travelled, in whole kilometres
fare int Monthly fare for that distance

Notes: fare is declared without a starting value, which is safe here only because the final else guarantees that some branch assigns it; remove that else and the program will no longer compile. Reversing the order of the tests would break the program without breaking the compilation — with distance <= 20 written first, a distance of 3 km would be charged 850.

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.

Q. In the ladder, would the program still be correct if the last else were written as else if (distance > 20)?

A. It would give the same fares, but it would no longer compile. The compiler cannot prove that some branch always assigns fare, so it reports that fare may not have been initialized. A bare else states that everything left over is covered; else if does not.

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.

largest of three numbers using nested if statements Problem: read three integers from the user and display the largest of them.

import java.util.Scanner;

class LargestOfThree
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int a, b, c, largest;

        System.out.println("Enter three numbers:");
        a = in.nextInt();
        b = in.nextInt();
        c = in.nextInt();

        if (a > b)
        {
            if (a > c)
                largest = a;
            else
                largest = c;
        }
        else
        {
            if (b > c)
                largest = b;
            else
                largest = c;
        }

        System.out.println("Largest = " + largest);
    }
}

Sample output, for the values 17, 42 and 30 typed by the user:

Enter three numbers:
17 42 30
Largest = 42

Variable description:

[Table:] Table 6.7 – variable description for the class LargestOfThree [/Table]

Variable Type Purpose
in Scanner Reads the three numbers typed by the user
a, b, c int The three numbers to be compared
largest int The largest of the three

Notes: the outer condition is a > b, so when the two are equal control takes the else branch and compares b with c — which gives the right answer, since equal values may be reported either way. The braces around each inner conditional are not optional here; the next box shows why.

an else that attaches to the wrong if What students write:

if (a > 0)
    if (b > 0)
        System.out.println("both positive");
else
    System.out.println("a is not positive");

What happens: the program compiles and runs, but the else belongs to the inner if, not the outer one, whatever the indentation suggests. With a positive and b negative the program prints that a is not positive; with a negative it prints nothing at all. This mis-attachment is known as the dangling else problem.

The fix: an else always pairs with the nearest if that has not already got one, so use braces to say which if you mean.

if (a > 0)
{
    if (b > 0)
        System.out.println("both positive");
}
else
    System.out.println("a is not positive");

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 : valueIfFalse

The 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;

Table 6.8 – the if else statement compared with the ternary operator

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.

Take the line String result = (marks >= 40) ? "Pass" : "Fail"; and rewrite it as an if else statement. Run both versions on the marks 39, 40 and 41 and check that the two outputs agree at every boundary value.

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;
}

writing a condition or a range as a case label What students write:

switch (marks)
{
    case marks >= 40:
        System.out.println("Pass");
        break;
}

What happens: the program does not compile. A case label is a constant that the switch value is compared with, not a test to be evaluated. The compiler reports a type mismatch, because marks >= 40 has type boolean where an int constant was required.

The fix: a switch can ask only “is the value equal to this?”. Any test involving a range belongs in an if else if ladder.

if (marks >= 40)
    System.out.println("Pass");

Q. May the same value appear as a case label twice in one switch?

A. No. The compiler reports a duplicate label and rejects the program. Since control jumps to the matching label, two labels of the same value would leave it with no way to decide which one to jump to.

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.

Q3. What actually happens in a switch case statement if you forget one break?

A. nothing stops control at the end of that case, so it continues into the statements of the case below it, and the one below that, until it meets a break or reaches the end of the switch block. Case labels are only entry points; they do not enclose their statements the way braces enclose a block. A missing break therefore does not skip a case or repeat one — it runs extra cases that were never meant to run, and the program compiles perfectly while doing it.

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.

number of days in a given month Input: a month number typed by the user. Output: the number of days in that month, or a message if the number is not a valid month. Method:

1. Read the month number.
2. Switch on the month number.
3. For 1, 3, 5, 7, 8, 10 and 12, set days to 31.
4. For 4, 6, 9 and 11, set days to 30.
5. For 2, set days to 28.
6. For anything else, set days to 0.
7. If days is 0, report an invalid month; otherwise display days.

Why it works: the twelve valid month numbers are divided into three groups, no value lies in more than one group, and the default covers every remaining value — so exactly one branch runs for every possible input. Cost: one match and one assignment, whatever the month. The work does not grow with the size of the input, so the cost is \(O(1)\).

number of days in a month, using grouped case labels Problem: read a month number from the user and display the number of days in that month, taking February as 28 days. Report an invalid month number.

import java.util.Scanner;

class DaysInMonth
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int month;
        int days;

        System.out.print("Enter the month number (1 to 12) : ");
        month = in.nextInt();

        switch (month)
        {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                days = 31;
                break;

            case 4:
            case 6:
            case 9:
            case 11:
                days = 30;
                break;

            case 2:
                days = 28;
                break;

            default:
                days = 0;
        }

        if (days == 0)
            System.out.println("Invalid month number");
        else
            System.out.println("Number of days : " + days);
    }
}

Sample output, for the value 9 typed by the user:

Enter the month number (1 to 12) : 9
Number of days : 30

Variable description:

[Table:] Table 6.9 – variable description for the class DaysInMonth [/Table]

Variable Type Purpose
in Scanner Reads the month number typed by the user
month int Month number supplied by the user
days int Number of days in that month; 0 marks an invalid month

Notes: the default branch is what allows days to be declared without a starting value, because every path through the switch now assigns it. Using 0 as a marker for “invalid” is safe only because no real month has zero days. The reporting is deliberately left outside the switch, where the choice is two-way and an if else reads better. The honest limitation is February: a month number alone cannot tell you whether the year is a leap year, so 29 days can never be produced. Correcting that needs the year as a second input and a test on it that a switch cannot express.

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.

Table 6.10 – the switch case statement compared with the if else if ladder

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.

Q. Could the bus fare program of section 6.4.3 be rewritten with a switch?

A. Not sensibly. Its tests are ranges, and a case label can name only one constant value, so you would need a separate label for every kilometre. It would be longer, harder to read, and would break the moment someone travelled further than the labels allowed for.

Take the library menu of section 6.5.1 and delete the break at the end of case 1. Predict the output for the choices 1, 2 and 5 before you run it, then run it and account for any difference between your prediction and what appears.

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.

NoteDefinition

A loop is a control statement in which a statement or block, called the body of the loop, is executed repeatedly for as long as a stated condition is true. Each execution of the body is one pass through the loop.

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)
    statement

The 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.

total and average of the marks of a class Input: the number of students, followed by that many marks. Output: the total of the marks and their average. Method:

1. Read the number of students, count.
2. Set total to 0.
3. Repeat count times:
       read the next mark;
       add it to total.
4. Set average to total divided by count, as a fraction.
5. Display total and average.

Why it works: total holds the sum of every mark read so far. That is true before the loop begins, when nothing has been read and the sum is 0, and every pass keeps it true by adding exactly the one new mark. When the loop ends every mark has been read, so total is the sum of them all. Cost: one pass per student, so the number of additions grows in step with the size of the class. The cost is \(O(n)\), where \(n\) is the number of students.

total and average of the marks of a class Problem: read the number of students and then their marks, and display the total and the average. Assume the number of students is at least one.

import java.util.Scanner;

class MarksTotal
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int count, i, marks, total;
        double average;

        System.out.print("How many students? ");
        count = in.nextInt();

        total = 0;
        System.out.println("Enter " + count + " marks:");
        for (i = 1; i <= count; i++)
        {
            marks = in.nextInt();
            total = total + marks;
        }

        average = (double) total / count;

        System.out.println("Total   : " + total);
        System.out.println("Average : " + average);
    }
}

Sample output, for the count 4 and the marks 62, 78, 45 and 90 typed by the user:

How many students? 4
Enter 4 marks:
62 78 45 90
Total   : 275
Average : 68.75

Variable description:

[Table:] Table 6.11 – variable description for the class MarksTotal [/Table]

Variable Type Purpose
in Scanner Reads the values typed by the user
count int Number of students whose marks are to be read
i int Loop control variable, counting the passes
marks int The mark read in the current pass
total int Sum of all marks read so far
average double Average of the marks

Notes: total is set to 0 before the loop, not inside it — set inside, it would be wiped clean at the start of every pass and would finish holding the last mark alone. The cast in (double) total / count matters just as much: without it both operands are integers, integer division discards the fraction, and an average of 68.75 would be reported as 68. The stated assumption is a real limitation — with a count of zero the loop never runs and the division has nothing meaningful to produce, so a complete program would check the count before using it.

the loop of MarksTotal, for count = 4 and the marks 62, 78, 45, 90

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)
    statement

There 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.

reversing the digits of a positive number Input: a positive integer. Output: the integer formed by writing its digits in the opposite order. Method:

1. Read the number.
2. Set reversed to 0.
3. While the number is greater than 0:
       set digit to the remainder of the number divided by 10;
       set reversed to reversed * 10 + digit;
       set the number to the number divided by 10, discarding any fraction.
4. Display reversed.

Why it works: dividing by 10 removes the last digit and the remainder recovers it, so the digits are peeled off from the right. Multiplying reversed by 10 before adding the new digit shifts everything already collected one place to the left, which lays the digits down in the opposite order. The number is strictly smaller after every pass, so it must reach 0 and the loop must end. Cost: one pass per digit. The cost is \(O(d)\), where \(d\) is the number of digits in the number.

reversing the digits of a positive number Problem: read a positive integer from the user and display the number formed by its digits in reverse order.

import java.util.Scanner;

class ReverseNumber
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int number, digit, reversed;

        System.out.print("Enter a positive number : ");
        number = in.nextInt();

        reversed = 0;
        while (number > 0)
        {
            digit = number % 10;
            reversed = reversed * 10 + digit;
            number = number / 10;
        }

        System.out.println("Reversed number : " + reversed);
    }
}

Sample output, for the value 4172 typed by the user:

Enter a positive number : 4172
Reversed number : 2714

Variable description:

[Table:] Table 6.12 – variable description for the class ReverseNumber [/Table]

Variable Type Purpose
in Scanner Reads the number typed by the user
number int The number being reversed; reduced digit by digit
digit int Last digit of the number in the current pass
reversed int The reversed number built up so far

Notes: the loop consumes number as it works, so the original value has gone by the time the loop ends; a program that needed to report “4172 reversed is 2714” would have to keep a copy before the loop began. Two limitations are worth stating plainly. A negative input makes the condition false immediately and the program reports 0. A number ending in zeros loses them, so 4100 is reported as 14 — which is arithmetically right and may still not be what the user expected.

the loop of ReverseNumber, for the input 4172

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.

the loop of ReverseNumber Here \(n\) is the value of the number entered and the counted operation is one pass of the loop. One pass removes exactly one digit, so a number of \(d\) digits costs \(d\) passes. Since \(d\) grows far more slowly than \(n\) — a thousandfold larger number needs only three more passes — the cost is \(O(d)\), not \(O(n)\).

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.

leaving out the semicolon after the condition of a do while loop What students write:

do
{
    total = total + i;
    i++;
}
while (i <= 10)

What happens: the program does not compile. The compiler reports a missing semicolon at the end of the while line, and often several further errors after it, because it can no longer tell where the statement ends.

The fix: the closing while of a do while loop ends the whole statement, so it takes a semicolon — unlike the while of an ordinary while loop, which is followed by a body.

while (i <= 10);

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’

Table 6.13 – the three looping statements compared

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

Q2. A while loop and a do while loop look almost identical. Is there any input for which they behave differently?

A. yes — every input for which the condition is false at the start. A while loop tests before the first pass, so it does nothing at all; a do while loop tests only after the first pass, so it has already run its body once by the time it finds out. For a mark of 55, the validation loop above prompts exactly once and stops, while a while loop with the same condition would never prompt at all. Once the condition is true at the start, the two behave identically for the rest of the run. The whole difference is one pass, and it is the first one.

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.

a stray semicolon after the header of a for loop What students write:

for (i = 1; i <= 5; i++);
    System.out.println(i);

What happens: the program compiles and prints one line, 6. The semicolon at the end of the header is an empty statement, and that empty statement is the body of the loop. The println is not part of the loop at all — it runs once, after the loop has finished and left i at 6.

The fix: remove the semicolon, so that the statement below becomes the body.

for (i = 1; i <= 5; i++)
    System.out.println(i);

This is the same punctuation trap as the missing braces in section 6.4.1, and it is harder to spot, because a semicolon is exactly what you have been trained to write at the end of a line.

Take the counted loop for (i = 1; i <= 5; i++) with System.out.println(i); as its body. Write three altered versions — one with the update removed, one starting i at 6, and one with the condition changed to i >= 1 — and predict what each will print before you run it. Be ready to stop a program that does not end.

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.

testing whether a number is prime Input: a whole number greater than 1. Output: a statement of whether that number is prime. Method:

1. Read the number.
2. Set prime to true.
3. For each i from 2 up to the number divided by 2:
       if i divides the number exactly:
             set prime to false;
             leave the loop.
4. If prime is still true, report the number as prime; otherwise report it as not prime.

Why it works: prime records the claim “no divisor has been found so far”, which is true before any division has been tried. Only the discovery of a divisor makes it false, and any divisor of a number other than the number itself is at most half of it — so a loop that finishes without setting prime to false has shown that no divisor exists. Cost: at worst one pass for every value from 2 up to half the number, so \(O(n)\), where \(n\) is the number being tested. When the number has a small divisor the break ends the loop far sooner.

testing whether a number is prime Problem: read a whole number greater than 1 and report whether it is a prime number.

import java.util.Scanner;

class PrimeCheck
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int number, i;
        boolean prime;

        System.out.print("Enter a number greater than 1 : ");
        number = in.nextInt();

        prime = true;
        for (i = 2; i <= number / 2; i++)
        {
            if (number % i == 0)
            {
                prime = false;
                break;
            }
        }

        if (prime)
            System.out.println(number + " is a prime number");
        else
            System.out.println(number + " is not a prime number");
    }
}

Sample output, for the value 91 typed by the user:

Enter a number greater than 1 : 91
91 is not a prime number

Variable description:

[Table:] Table 6.14 – variable description for the class PrimeCheck [/Table]

Variable Type Purpose
in Scanner Reads the number typed by the user
number int The number being tested
i int Loop control variable; the divisor being tried
prime boolean Records whether a divisor has been found

Notes: prime is what allows the loop to report its finding after it has ended — a break leaves the loop but carries no information out with it, so something must be set before jumping. The upper limit number / 2 is safe because no number has a divisor greater than half of it apart from itself. For 2 and 3 the loop never runs at all, and both are correctly reported as prime. The stated restriction is a real one: given 1, the loop does not run and the program reports 1 as prime, so a complete program would reject any input below 2.

the loop of PrimeCheck, for the input 91

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.

Q. A switch is written inside a loop and one of its cases ends with a break. Does that break leave the switch or the loop?

A. The switch. A break ends the innermost statement that can be broken out of, and here that is the switch. The same rule governs nested loops: a break in an inner loop ends the inner loop only, and the outer loop carries on with its next pass.

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.

a continue that skips the update of a while loop What students write:

i = 1;
while (i <= 10)
{
    if (i == 5)
        continue;
    System.out.println(i);
    i++;
}

What happens: the program prints 1, 2, 3 and 4, and then runs for ever. When i reaches 5 the continue returns to the condition without ever reaching i++, so i stays at 5 and the same jump is made on every pass after that.

The fix: make sure every path through the body advances the loop control variable, by updating it before the continue can be reached.

i = 0;
while (i < 10)
{
    i++;
    if (i == 5)
        continue;
    System.out.println(i);
}

Table 6.15 – the break statement compared with the continue statement

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

Q4. Both break and continue interrupt a loop. Do they hand control back to the same place?

A. no, and that difference is the whole reason for having two statements. A break hands control to the first statement after the loop: the condition is never tested again and no further pass is made. A continue hands control back to the top of the loop — in a for loop the update runs, the condition is tested, and if it is still true the next pass begins exactly as usual. So break abandons the loop, while continue abandons only the remainder of one pass. Each acts on the innermost loop that contains it.

Practical link: this assignment exercises looping statements together with break and continue, and belongs in the work file under conditional and looping statements. Write a program that reads a whole number greater than 1 and displays all its factors other than 1 and the number itself, using continue to pass over those two values when the loop reaches them. If no such factor exists, display that the number is prime. Test the program with 36, and then with the unseen input 97.

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.

printing a triangle of numbers Input: the number of rows to print. Output: that many lines; the line numbered i contains the numbers 1 to i, separated by spaces. Method:

1. Read the number of rows.
2. For each i from 1 to rows:
       for each j from 1 to i:
             print j followed by a space;
       move to a new line.

Why it works: the inner loop is bounded by i, the outer counter, so each line is exactly as long as its line number. The new line is printed by the outer loop, after the inner loop has finished, so it happens once per line and not once per number. Cost: the inner loop makes 1 pass, then 2, and so on up to \(n\), giving \(n(n + 1)/2\) passes in all. The dominant term is \(n^2/2\), so the cost is \(O(n^2)\), where \(n\) is the number of rows.

printing a triangle of numbers Problem: read the number of rows from the user and print a triangle in which the line numbered i contains the numbers 1 to i, separated by single spaces.

import java.util.Scanner;

class NumberTriangle
{
    public static void main(String args[])
    {
        Scanner in = new Scanner(System.in);
        int rows, i, j;

        System.out.print("Enter the number of rows : ");
        rows = in.nextInt();

        for (i = 1; i <= rows; i++)
        {
            for (j = 1; j <= i; j++)
                System.out.print(j + " ");

            System.out.println();
        }
    }
}

Sample output, for the value 4 typed by the user:

Enter the number of rows : 4
1
1 2
1 2 3
1 2 3 4

Variable description:

[Table:] Table 6.16 – variable description for the class NumberTriangle [/Table]

Variable Type Purpose
in Scanner Reads the number of rows typed by the user
rows int Number of lines to be printed
i int Outer loop control variable; the line being printed
j int Inner loop control variable; the number being printed on that line

Notes: the two output statements are doing different jobs and belong at different levels. System.out.print keeps the cursor on the same line, so the numbers of one row appear side by side; System.out.println() with nothing in its brackets ends the line, and it is placed in the outer body — put it inside the inner loop instead and every number would appear on a line of its own. The condition of the inner loop is j <= i, not j <= rows, and that single character is what makes the shape a triangle rather than a rectangle.

the nested loops of NumberTriangle, for rows = 3

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.

the nested loops of NumberTriangle Here \(n\) is the number of rows and the counted operation is one execution of the inner body. The inner loop runs \(1 + 2 + \dots + n\) times, which is \(n(n + 1)/2\), or \(n^2/2 + n/2\). The dominant term is the one in \(n^2\), so the cost is \(O(n^2)\): doubling the number of rows roughly quadruples the work.

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.

initialising the inner loop control variable outside the outer loop What students write:

i = 1;
j = 1;
while (i <= rows)
{
    while (j <= i)
    {
        System.out.print(j + " ");
        j++;
    }
    System.out.println();
    i++;
}

What happens: the program compiles and prints the first line correctly, then nothing but blank lines. After the first outer pass j has reached 2 and is never reset, so on every later pass the inner condition j <= i is tested with a j that has already outrun it, and the inner loop makes no passes at all.

The fix: move the initialisation of the inner loop control variable inside the outer body, so that it is done again for every outer pass.

while (i <= rows)
{
    j = 1;
    while (j <= i)
    ...

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.

Q. In a nested loop, which variable changes faster — the outer one or the inner one?

A. The inner one, by a long way. It runs through its whole range before the outer variable moves on by a single step, exactly as the minute hand of a clock completes a full circle for each hour the hour hand advances.

Change the inner condition of the triangle program from j <= i to j <= rows and run it for 4 rows. Describe in one sentence what shape is now printed, and say which of the two loops decides the number of lines and which decides their length.

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.

putting the update at the top of the body when converting a for loop What students write:

i = 7;
while (i <= 70)
{
    i = i + 7;
    System.out.print(i + " ");
}

What happens: the program runs and prints ten values, so the count is right and the fault is easy to miss. But the values are wrong at both ends — the line begins at 14 instead of 7 and finishes at 77 instead of 70. The update has been applied before the value was used, so every number printed is one step ahead of the one intended.

The fix: keep the order of the original for loop, in which the body uses the value and the update prepares the next one.

while (i <= 70)
{
    System.out.print(i + " ");
    i = i + 7;
}
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.

Q. If the three loops can all do the same work, does the choice matter at all?

A. Not to the compiler, and a great deal to the reader. The construct you choose announces your intention: a for loop says the number of passes is known, a while loop says it is not, and a do while loop says the body must run before it can be judged. Choosing the one that matches the situation is part of writing the program correctly, not a matter of taste.

Take the validation loop of section 6.6.4 and rewrite it as a while loop, choosing a starting value for marks that makes the loop behave identically. Then explain, in two lines, why that starting value must be one the user could never legitimately type.

6.10 Summary

Statements and blocks
  1. 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.

  2. 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.

  3. 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.

  4. An assignment evaluates the expression on the right and stores the result in the variable on the left. This is why count = count + 1 is sensible as an instruction though absurd as an equation: the old value is used to compute the new one.

  5. Java has three forms of comment — the single line comment, the multiline comment, and the documentation comment, which the javadoc tool of the JDK reads.

  6. 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
  1. 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.

  2. 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.

  3. 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
  1. if runs its statement or skips it. if else runs exactly one of two alternatives — never both and never neither.

  2. An if else if ladder tests its conditions strictly in order and stops at the first true one, so the order of the rungs is part of the logic. Independent if statements are all tested, and where their conditions overlap the last assignment wins.

  3. A nested if asks a second question only after the first has been answered. An else pairs with the nearest unmatched if, whatever the indentation suggests; braces are what prevent a dangling else.

  4. 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
  1. A switch matches one expression for equality against constant case labels. It accepts byte, short, char, int, their wrapper classes and enumerated types, and String from Java 7 onwards; it accepts neither ranges nor conditions as labels.

  2. Case labels are entry points, not compartments. Control enters at the matching label and continues through the labels below it until a break stops it — that continuation is fall through, useful when stacked labels share one action and a bug when a break has simply been forgotten.

  3. The default case catches every value that matches no label, as the final else does in a ladder. Fixed values suit a switch; ranges and multi-variable tests suit a ladder.

Looping statements
  1. 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.

  2. The for loop carries three of the four parts in its header and suits a known number of passes. The while loop tests before each pass and suits an unknown number. The do while loop tests after each pass, so its body always runs at least once.

  3. for and while are entry controlled and may make zero passes; do while is exit controlled and makes at least one. The closing while of a do while loop takes a semicolon.

  4. 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.

  5. 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
  1. break ends the innermost loop or switch containing it and passes control to the statement after it. continue abandons only the current pass and returns to the top of the loop.

  2. In a for loop the update runs even after a continue, because it sits in the header. In a while loop a continue written above the update skips it, and the loop never ends.

  3. Testing a number for primeness by trial division costs \(O(n)\) in the worst case, where \(n\) is the number tested. A break on the first divisor found improves the typical case but not the worst case, since a prime number has no divisor to find.

  4. 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 for header, and your responsibility in a while loop.

  5. 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)\).

  6. 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 while loop needs a guarding if, because a do while loop cannot make zero passes.

6.11 Key Terms

  1. assignment statement — a statement that evaluates the expression on its right and stores the result in the variable named on its left.

  2. block — zero or more statements enclosed in braces, treated by the language as a single statement.

  3. break statement — a jump statement that ends the innermost loop or switch containing it, passing control to the statement after it.

  4. case label — a constant value written in a switch statement, marking the point at which control enters when the switch expression equals that value.

  5. comment — text written for a human reader and removed by the compiler, so that it can never affect what the program does.

  6. compound assignment operator — an operator such as += or *= that combines an arithmetic operation with an assignment to the same variable.

  7. condition — an expression of type boolean, whose value decides what a conditional or looping statement does next.

  8. conditional statement — a control statement that tests a condition and selects which of the statements under its control are executed.

  9. control statement — a statement whose purpose is to decide which statements run next, rather than to compute a value.

  10. dangling else — the mis-attachment that occurs when an else pairs with a nearer if than the writer intended, since it always pairs with the nearest if that has none.

  11. declaration statement — a statement that introduces a name into the program and fixes the type of value it may hold.

  12. default case — the label in a switch statement to which control passes when the switch expression matches no case label.

  13. do while loop — a loop written with the do while statement, in which the condition is tested after each pass, so the body always runs at least once.

  14. documentation comment — a comment beginning with /**, written above a class or method and read by the javadoc tool to generate documentation.

  15. empty loop — a loop whose body is an empty statement or an empty block, and which therefore repeats without doing any work.

  16. empty statement — a statement consisting of a semicolon alone, which does nothing.

  17. entry controlled loop — a loop whose condition is tested before each pass, so that it may make no passes at all; the for and while loops are of this kind.

  18. executable statement — a statement that produces machine code and performs an action when the program runs.

  19. exit controlled loop — a loop whose condition is tested after each pass, so that it always makes at least one; the do while loop is of this kind.

  20. fall through — the continuation of control from one case of a switch statement into the case below it, when no break stops it.

  21. flow of control — the order in which the statements of a program are carried out while it runs.

  22. for loop — a loop written with the for statement, which carries the initialisation, the test condition and the update in a single header.

  23. if else if ladder — a chain of conditionals in which each else part holds the next if, the conditions being tested in order until one is found true.

  24. infinite loop — a loop whose condition never becomes false, so that it continues until the program is stopped.

  25. iteration — the repeated execution of a group of statements while a condition remains true.

  26. JDK — Java Development Kit, the set of tools with which Java programs are compiled, run and documented.

  27. lifetime — the period during execution for which a variable’s storage exists; for a local variable, from its declaration until control leaves its block.

  28. local variable — a variable declared inside a method or a block, and available only within it.

  29. loop — a control statement in which a statement or block is executed repeatedly while a stated condition is true.

  30. loop control variable — the variable whose value is set at the start of a loop, tested by its condition and changed by its update.

  31. multiline comment — a comment beginning with /* and ending with */, which may extend over several lines.

  32. nested if statement — an if statement written inside the if part or the else part of another if statement.

  33. 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.

  34. non-executable statement — a statement that performs no action when the program runs, such as a declaration or a comment.

  35. scope — the region of the program text within which a name may be used.

  36. selection — the choosing of one group of statements to execute in place of another, according to a condition.

  37. single line comment — a comment beginning with // and continuing to the end of that line.

  38. statement — one complete instruction in a program, formed from valid tokens and, if simple, ended by a semicolon.

  39. switch case statement — a control statement that matches one expression for equality against constant case labels and jumps to the label that matches.

  40. ternary operator — the operator written ? :, which takes three operands and yields one of two values according to a condition.

  41. token — the smallest meaningful piece of a program: a keyword, a name, a literal, an operator or a punctuation mark.

  42. visibility — which names a given statement can reach, namely those in scope in its own block and in the blocks enclosing it.

  43. while loop — a loop written with the while statement, 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

  1. 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?

  2. In a switch statement, control falls through to the next case unless a break stops it — and a forgotten break is 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?

  3. 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?

  4. Ending the search for a divisor with break makes 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?

  5. 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?

  6. Almost every fault in this chapter — the missing braces, the stray semicolon, the dangling else, the forgotten break — 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

  1. Which of the following is an exit controlled loop?
  1. for
  2. while
  3. do while
  4. None of these [1 mark]
  1. The least number of times the body of a while loop can be executed is:
  1. 0
  2. 1
  3. 2
  4. It depends on the update [1 mark]
  1. Which of the following types may not be used as the expression of a switch statement?
  1. int
  2. char
  3. double
  4. String [1 mark]
  1. When a continue statement is executed inside a for loop, control passes next to:
  1. The statement after the loop
  2. The update in the loop header
  3. The first statement of the body
  4. The condition, without the update [1 mark]
  1. A variable declared inside the body of a loop goes out of scope at:
  1. The end of the method
  2. The closing brace of the loop body
  3. The last pass of the loop
  4. The end of the class [1 mark]
  1. Assertion (A): A do while loop executes its body at least once, whatever its condition. Reason (R): The condition of a do while loop is tested only after the body has been executed. Choose the correct option:
  1. Both Assertion (A) and Reason (R) are true and Reason (R) is the correct explanation of Assertion (A).
  2. Both Assertion (A) and Reason (R) are true and Reason (R) is not the correct explanation of Assertion (A).
  3. Assertion (A) is true but Reason (R) is false.
  4. Assertion (A) is false but Reason (R) is true. [1 mark]
  1. 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]

  1. 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]

  2. State whether the following is true or false: a case label in a switch statement may be a variable. [1 mark]

  3. Give the full form of JDK. [1 mark]

6.14 Check Your Concepts

  1. Define a block. State one reason why braces are needed when more than one statement is to be governed by an if. [2 marks]

  2. Define the scope of a variable. Give one example of a variable whose scope is a single loop. [2 marks]

  3. What is meant by fall through in a switch case statement? State one situation in which it is deliberately used. [2 marks]

  4. Differentiate between an entry controlled loop and an exit controlled loop, naming one Java statement of each kind. [3 marks]

  5. Differentiate between the break statement and the continue statement as used inside a loop. [3 marks]

  6. Rewrite the following loop using a while loop, without changing what it prints.

for (int k = 20; k >= 5; k = k - 3)
    System.out.print(k + " ");

[3 marks]

  1. 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 if ladder affects the result, and state what would happen if the three tests were written as three separate if statements instead. [4 marks]

  2. 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]

  3. Compare the switch case statement with the if else if ladder 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

  1. Predict the output of the following segment, and state the value of i after 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]

  1. 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]

  1. 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]

  1. 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]

  1. 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

  1. 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]

  2. Write a menu driven program using a switch case statement 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]

  3. 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 4 and the last line is 1. Submit the complete class and a variable description table. [5 marks]

  4. 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 continue statement, 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]