Statements

A statement describes a state-changing action to be preformed by the processor. Unlike an expression which is "evaluated" for its value, a statement is "executed" for its effect. Executing a statement generally results in a change in the system state: for instance, the value of some variable is changed.

Statements can be simple or composite. Composite statements have other statements as constituents.

Empty statement

The simplest statement is the empty statement, sometimes called the null statement, skip statement, or noop. The empty statement just consists of a semicolon:

    ;

Executing the empty statement has no effect.

The empty statement is occasionally used as the body of a loop in which all the "action" takes place in the heading. For instance, the following loop searches for the first occurrence of a space in the String s.

    int i;
    for (i=0; i < s.length() && s.charAt(i) != ' '; i=i+1);

Note that the body of the loop is the empty statement.

Method invocations

A method invocation followed by a semicolon constitutes a statement. For instance, if v denotes an instance of java.util.Vector,

    v.removeElementAt(0);

is a statement that invokes the Vector method removeElementAt.

Typically, a method invoked in this way is a command: that is, its return type is specified as "void." However, a value-returning method can also invoked as a command. In this case, the value returned by the method invocation is simply ignored. For example, the class Vector has a method specified as

    public boolean remove (Object o)

Invoking the method removes the first occurrence of the specified object from the Vector. The returned value indicates whether or not the Vector contained the object. An invocation of this method as a statement

    v.remove(obj);

ignores the returned value.

A constructor invocation can also constitute a statement, though this construct is not commonly used:

    new JFrame("Top Window");

The reference value returned by the constructor is ignored.

Assignment statements

The prototypical statement is assignment. An assignment statement consists of a variable denotation ("left-hand side"), assignment operator (=), and expression ("right-hand side"), terminated by a semicolon:

variable = expression ;

To execute an assignment, the processor evaluates the expression and then stores the resulting value in the variable. The previous value of the variable is replaced by the new value.

The variable can be denoted by a simple identifier, a qualified identifier, or an array component reference. For example,

    i = i + 1;
    point.x = point.y;
    this.area = room.width() * room.length();
    elements[size-1] = obj;

Note that the variable appearing on the left hand side of the assignment can also appear in the expression on the right hand side. The first example above increments the value of the variable i by one.

There are eleven other assignment operators that incorporate binary operators. They are:

    *=    /=    %=    +=    -=    <<=    >>=    >>>=    &=    ^=    |=

The effect of executing

    i *= 2;

is essentially the same as executing

    i = i*2;

Other assignment operators work in a similar way. If expr1 and expr2 are expressions, then

expr1 op= expr2;

is equivalent to

expr1 = (expr1) op (expr2);

except that expr1 is evaluated only once.

Note that an assignment statement is an assignment expression followed by a semicolon.

Increment and decrement statements

An increment or decrement expression followed by a semicolon is an increment or decrement statement. An increment expression is a numeric variable preceded or followed by an increment operator (++). A decrement expression is a numeric variable preceded or followed by a decrement operator (--). For example, if i is an int variable,

    i++;
    ++i;
    i--;
    --i;

are statements. The first two increment the value of i by one, and the last two decrement the value of i by one. Note that since the value resulting from evaluation of the expression is ignored, it does not matter whether the operators are used in prefix or postfix form.

Return statements

A return statement consists of the keyword return followed by an expression and ending with a semicolon:

    return count+1;

Executing a return statement terminates execution of the method containing the statement. The value of the expression is the value returned by the method. The type of the expression must be compatible with the return type of the method.

In a value-returning method - that is, in a method not specified as void - every possible execution sequence must end with a return statement. For instance, consider the following:

    public int advance (int a) {
        if (a == 0)
            return 1;
        else if (a == 1)
            return 0;
    }

If the value of the parameter a is neither 0 not 1, the method will terminate without executing a return statement. Therefore this method is not legal.

Note that the method would not compile even if the second condition was a != 0. The compiler simply checks paths through the method without attempting to verify whether or not each path is actually possible at run time. This is illustrated in the figure below.

 

A method that does not return a value - that is, a method specified as void - or a constructor can contain a return statement that does not contain an expression:

    return;

Execution of this statement terminates the method. We recommend against using this form of the return statement.

Blocks

A block is a composite statement. It is made up of a sequence of statements enclosed in braces. To execute a block, the processor executes the component statements in order. For instance, the following block interchanges the values of the int variables a and b:

    {
        a = b-a;
        b = b-a;
        a = a+b;
    }

A block is commonly used in contexts where the language syntax allows only a single statement: the body of a loop, for example.

A block can contain local variable declarations as well as statements. For example, the following block also interchanges the values of a and b:

    {
        int temp;
        temp = a;
        a = b;
        b = temp;
    }

The scope of a local variable is from its declaration to the end of the block.

A block can be empty: that is, it need contain no statements or declarations. Empty blocks are sometimes used as method body "stubs" during initial program development.

Conditional statements

Conditional statements are also composite statements. A conditional statement describes several possible actions, one of which is to be performed at run time.

If-then statements

An if-then statement is composed of a boolean expression (or "condition") and a component statement:

if ( booleanExpression )
    
statement

Note that the parentheses are required. The boolean expression is evaluated, and if true, the component statement is executed. Thus the component statement is executed or not depending on the value of the boolean expression. The boolean expression is sometimes said to "guard" execution of the statement.

The following statement doubles the value of the int variable i if i is even, and does nothing if i is odd.

    if (i%2 == 0)
        i = 2*i;

The syntax requires a single statement following the boolean expression. If several actions must be performed, a block can be used. For instance, the following swaps the values of the int variables a and b if b is larger than a.

    if (b > a) {
        int temp = a;
        a = b;
        b = temp;
    }

In the following, the first assignment to a is done only if b is larger than a, but the second and third assignments are done in either case. Despite the misleading indention, they are not part of the if-then statement and are not guarded by the condition b > a.

    if (b > a)
        a = b-a;
        b = b-a;
        a = a+b;

If-then-else statements

An if-then statement offers a choice between performing or not performing an action. An if-then-else offers a choice of two possible actions. It consists of a boolean expression and two component statements.

if ( booleanExpression )
    
statement1
else
    
statement2

The boolean expression is evaluated, and if true, the first statement is executed. If false, the statement following the else is executed.

The following assigns max the larger of a and b:

    if (a > b)
        max = a;
    else
        max = b;

Because of a possible ambiguity, an if-then cannot be the "true" alternative of an if-then-else. That is, in

    if (a > b)
        if (a > c)
            n = a;
        else
            n = c;

the else goes with the inner if: the if-then-else is nested in the if-then.

If the outer statement is to be an if-then-else, the inner if-then must be converted to a block by enclosing it in braces:

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

A cascade of if-then-else statements is often used to select one of several alternative. For instance, the following increments one of five variables, depending on the value of suit.

    if (suit == 1)
        clubCount = clubCount + 1;
    else if (suit == 2)
        diamondCount = diamondCount + 1;
    else if (suit == 3)
        heartCount = heartCount + 1;
    else if (suit == 4)
        spadeCount = spadeCount + 1;
    else
        invalidCount = invalidCount + 1;

Switch statements

A switch is a rather baroque structure originally derived from an assembly language technique called a "branch table." Like a cascade of if-then-else statements, a switch offers a number of possible alternative actions. However, the actions are not independent. What the switch offers is a sequence of actions that can be started at various points.

A switch statement consists of an integer, character, or boolean valued expression and a switch block:

switch ( expression )
    
switchBlock

A switch block consists of labeled statement groups enclosed in braces:

{ labeledStatementGroup ... }

A labeled statement group consists of a possibly empty sequence of statements preceded by one or more labels, where a label has one of the following forms:

case constantExpression :
default :

The label default can appear at most once in a switch block, and must be the last label in the block. For example

    switch (suit) {
    case 1:  clubCount = clubCount + 1;
             break;
    case 2:  diamondCount = diamondCount + 1;
             break;
    case 3:  heartCount = heartCount + 1;
             break;
    case 4:  spadeCount = spadeCount + 1;
             break;
    default: invalidCount = invalidCount + 1;
    }

The expression is evaluated, and execution continues with the statement following the label that matches the value of the expression. If no label matches the value of the expression, execution continues at the default label. If there is no default label, execution of the switch statement is complete. The above example increments a count depending on the value of suit. If the value of suit is 3, for instance, execution begins with the assignment incrementing heartCount.

Note that execution of the switch is not terminated by reaching another label. Execution is complete only if no label matches the value of the expression and there is no default label, a break statement is executed (see below), or end of the switch block is encountered. Also note that a labeled statement group can have more than one label. Consider the following example.

    switch (suit) {
    case 2:
    case 3:  redCardCount = redCardCount + 1;
             break;
    case 4:  spadeCount = spadeCount + 1;
    case 1:  blackCardCount = blackCardCount + 1;
             break;
    default: invalidCount = invalidCount + 1;
    }

If the value of suit is 4, three statements are executed: the assignment to spadeCount, the assignment to blackCardCount, and finally the break.

Loop statements

Loops statements specify an action that is to be repeatedly performed a number of times. Loops are also referred to as "iterations."

While statements

A while statement is the simplest and safest loop statement. It composed of a boolean expression and a statement, the loop body:

while ( booleanExpression )
    
statement

The boolean expression is evaluated and if true, the statement is executed and the process repeated. The expression and the statement are repeatedly executed until the expression evaluates to false.

Clearly, if the while statement is to terminate, it must be possible to change the value of the boolean expression by executing the body statement.

The following loop doubles the value of the int variable i until it's greater than limit:

    while (i <= limit)
        i = 2*i;

The body of while statement is often a block or other structured statement, as illustrated by the following examples.

The body of the while statement in this example is an if-then-else. The method computes the greatest common divisor of two positive integers.

    /**
     * Greatest common divisor of the specified integers.
     *   require:
     *     a > 0 && b > 0
     */
    int gcd (int a, int b) {
        while (a != b)
            if (a > b)
                a = a-b;
            else
                b = b-a;
        return a;
    }

The (partial) correctness of the above method can be seen by observing that:

Termination follows from the invariance of max(a,b) > 0, and the fact that max(a,b) is decremented by each iteration.

In the next example, the body of the while statement is a block. The method computes the n-th element of the Fibonacci sequence. The sequence starts "1, 1, ..." and thereafter each element is the sum of the previous two:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ...
    /**
     * The n-th Fibonacci number, where the "1st" is 1,
     * the "2nd" is 1, etc.
     *   require:
     *     n >= 1
     */
    int fib (int n) {
        int n1 = 0;
        int n2 = 1;     // n2 is the ith Fibonacci number
        int i = 1;
        while (i < n) {
            n2 = n2 + n1;
            n1 = n2 - n1;
            i = i + 1;
        }
        return n2;
    }

Do statements

Like a while statement, a do statement is also composed of a boolean expression and a statement:

do
    statement
while ( booleanExpression )

The statement is executed, and then the boolean expression is evaluated. If true, the process is repeated. The statement and the expression are repeatedly executed until the expression evaluates to false.

Note that the body of the do statement is always executed at least once. For instance, the following loop doubles i at least once:

    do
        i = 2*i;
    while (i <= limit)

Use of the do statement is not very common. The following method builds a string representation of an integer value. Digits are obtained, right to left, by successively dividing the integer by 10 and converting the remainder to a character. The method depends on the fact that the character code for '1' is one more than the code for '0', the code for '2' is two more than '0', etc. A do statement is useful here, since at least one character must always be generated, even if the integer argument is 0.

    /**
     * Representation of the specified integer as a String
     * of decimal digits.
     *   require:
     *     n >= 0
     */
    String intToString (int n) {
        String s = "";
        do {
            char digit = (char)(n%10+'0');
            s = digit + s;
            n = n/10;
        } while (n != 0);
        return s;
    }

For statements

A for statement is a while loop with initialization and update built into the heading. A for statement is often used to implement a "counting loop": a loop for which we know how many times the body is to be executed. In such a case, we need only count the loop iterations and stop when the appropriate number has been reached.

A for statement consists of a "for clause" and a body statement. The for clause contains an initialization component, a boolean expression, and an update component, separated by semicolons. Any of these can be empty.

for ( initialization ; booleanExpression ; update )
    
statement

The initialization consists of a possibly empty comma separated sequence of assignment statements, increment statements, decrement statements, and/or local variable declarations. The scope of any local variable declared in the initialization is the for statement.

The update consists of a possibly empty comma separated sequence of assignment statements, increment statements, and/or decrement statements.

The ending semicolons are omitted from statements in the initialization or update.

When the for statement is executed, the initialization statements are done first. Then the boolean expression is evaluated. If true, the body statement and then update statements are executed, and the boolean expression is again evaluated. The boolean expression, body statements, and update statements are repeatedly executed until the boolean expression evaluates to false.

The for statement is roughly equivalent to

initialization;
while (
booleanExpression ) {
    
statement;
    
update;
}

As an example, the following method determines whether or not a specified string is a palindrome. (A palindrome is a string that reads the same forward or backward, such as "amanaplanacanalpanama".)

    /**
     * The specified String is a palindrome.
     */
    boolean isPalindrome (String s) {
        boolean isPalindrome;
        for (int i=0, int j=s.length()-1, isPalindrome=true;
             i<j && isPalindrome;
             i=i+1, j=j-1)
            if (s.getChar(i) != s.getChar(j))
                isPalindrome = false;
        return isPalindrome;
    }

The initialization part of the for statement consists of two declarations and an assignment. The update consists of two assignments. The scope variables declared in the initialization is the for statement. Thus the return statement could not be written

    return i >= j;

The body of a for statement is sometimes an empty statement, as in the following example.

    /**
     * The specified char is in the specified String.
     */
    boolean isIn (char c, String s) {
        int i;
        for (i=0; i<s.length() && s.getChar(i)!=c; i=i+1) ;
        return i<s.length();
    }

Note that the above method would not be correct if the for statement was written:

    for (int i=0; ...

The scope of the local variable i is the for statement, and i cannot be referenced in the return statement.

Break, continue, and labeled statements

Break statements

A break statement exits the innermost containing switch or loop statement. It consists of the keyword "break" followed by a semicolon:

    break;

The following method returns a string equal to its argument with trailing spaces, tabs, and newlines removed. The break causes the loop to be exited when the right-most non-blank, non-tab, non-newline is encountered. The method length returns the length of the string, getChar(n) the character with index n, and substring (start, end) a new string that contains the characters from index start through index end-1.

    /**
     * A String equal to the specified String with trailing
     * blanks, tabs, and newlines removed.
     */
    String trim (String s) {
        int n;
        for (n = s.length()-1; n >= 0; n--) {
            char c = s.getChar(n);
            if (c != ' ' && c != '\t' && c != '\n')
                break;
        }
        return s.substring(0,n+1);
    }

We do not recommend the structure illustrated by this example.

Continue statements

A continue statement terminates the current iteration of a loop body. It consists of the keyword "continue" followed by a semicolon:

    continue;

For example, the following loop "skips" repeated items in a list.

    int i;
    for (i == 0; i < list.size(); i=i+1) {
        if (i>0 && list.get(i).equals(list.get(i-1)))
            continue;
        // process list.get(i)
        ...
    }

We do not recommend the structure illustrated by this example.

Labeled statements

A statement can be prefixed with a label, consisting of an identifier followed by a colon. For instance:

    outerLoop: while ( ... ) ...

A break or continue statement can include a label:

    break outerLoop;
    continue outerLoop;

Execution of a break statement with a label causes termination of the labeled statement. The break statement must be included in the labeled statement.

Execution of a continue statement with label causes termination of the current iteration of the labeled statement. The labeled statement must be a loop, and the continue statement must be included in the labeled statement.

The following method determines whether or not two strings have a character in common. The break statement is used to terminate the outer loop when a character common to both strings is found.

    /**
     * The specified Stings have a character in common.
     */
    boolean haveCommonChar (String s1, String s2) {
        int i, j;
        outerLoop:
        for (i = 0; i < s1.length(); i++)
            for (j = 0; j < s2.length(); j++)
                if (s1.getChar(i) == s2.getChar(j))
                    break outerLoop;
        return i < s1.length();
    }

We do not recommend the structure illustrated by this example.

Throw statements

A throw statement causes an exception to be thrown. It consists of the keyword "throw" followed by an expression and terminated by a semicolon.

throw expression ;

The expression following the keyword throw must denote an exception. For example,

    throw new RuntimeException();
    throw e;

Throwing an exception causes the normal sequence of execution to cease. If the exception is thrown in a try statement (see below), the catch clauses of the try statement are examined in order to see if any match the type of the exception thrown. If a match is found, that catch clause is executed. If no match is found, or if the exception is not thrown in a try statement, the exception propagates to the calling method. That is, the exception is again thrown at the method invocation statement. See chapter 18 of Niño-Hosch for a detailed explanation of the throw statement.

Try statements

A try statement consists of a try block followed one or more catch clauses.

A try block consists of the keyword "try" followed by a block:

try {
    
statements and/or declarations
}

A catch clause consists of the keyword "catch" followed by a parameter declaration in parentheses, and a block:

catch ( parameterDeclaration ) {
    
statements and/or declarations
}

Catch clause parameters are exceptions. To execute a try statement, the statements of the try block are first executed. If they complete normally, the try statement is complete. If an exception is thrown during execution of the try block, the first catch clause whose parameter is compatible with the thrown exception is executed. If there is no compatible catch clause, the exception is propagated to the caller. If the catch clause completes normally, the try statement is complete. If the statements of the catch clause throw an exception, the exception is propagated to the method caller. (Unless the try statement itself is included in another try statement, in which case the exception might be caught by the enclosing try statement.) See the section on catching exceptions or chapter 18 of Niño-Hosch for a detailed explanation of the try statement.

In the following example, data is an input stream reader, and its method readInt throws an EOFException if end of file has been reached. The try statement processes data from the input stream until the end of file exception is thrown, and then closes the stream.

    try {
        while (true) {
            data.readInt();
            int i = data.lastInt();
            process(i);
        }
    } catch (EOFException e) {
        data.close();
    }

We do not recommend the structure illustrated by this example.

A try statement can be optionally followed by a finally clause, consisting of the keyword "finally" followed by a block:

finally {
    
statements and/or declarations
}

A finally clause is executed upon normal completion of the try block or of a catch clause.


Fred Hosch
Last modified: Tue Aug 7 08:21:34 CDT 2001