Statements and Expressions

Java statements appear inside methods and classes; they describe all activities of a Java program. Variable declarations and assignments, such as those in the previous section, are statements, as are basic language structures such as if/then conditionals and loops.

    int size = 5;
    if ( size > 10 )
        doSomething();
    for( int x = 0; x < size; x++ ) { ... }

Expressions produce values; an expression is evaluated to produce a result that is to be used as part of another expression or in a statement. Method calls, object allocations, and, of course, mathematical expressions are examples of expressions. Technically, because variable assignments can be used as values for further assignments or operations (in somewhat questionable programming style), they can be considered to be both statements and expressions.

    new Object();
    Math.sin( 3.1415 );
    42 * 64;

One of the tenets of Java is to keep things simple and consistent. To that end, when there are no other constraints, evaluations and initializations in Java always occur in the order in which they appear in the code—from left to right, top to bottom. We’ll see this rule used in the evaluation of assignment expressions, method calls, and array indexes, to name a few cases. In some other languages, the order of evaluation is more complicated or even implementation-dependent. Java removes this element of danger by precisely and simply defining how the code is evaluated. This doesn’t mean you should start writing obscure and convoluted statements, however. Relying on the order of evaluation of expressions in complex ways is a bad programming habit, even when it works. It produces code that is hard to read and harder to modify.

Statements and expressions in Java appear within a code block. A code block is syntactically a series of statements surrounded by an open curly brace ({) and a close curly brace (}). The statements in a code block can include variable declarations and most of the other sorts of statements and expressions we mentioned earlier:

    {
        int size = 5;
        setName("Max");
        ...
    }

Methods, which look like C functions, are in a sense just code blocks that take parameters and can be called by their names—for example, the method setUpDog():

    setUpDog( String name ) {
        int size = 5;
        setName( name );
        ...
    }

Variable declarations are limited in scope to their enclosing code block—that is, they can’t be seen outside of the nearest set of braces:

    {
        int i = 5;
    }

    i = 6;           // Compile-time error, no such variable i

In this way, code blocks can be used to arbitrarily group other statements and variables. The most common use of code blocks, however, is to define a group of statements for use in a conditional or iterative statement.

The most common form of the Java switch statement takes an integer (or a numeric type argument that can be automatically “promoted” to an integer type), a string type argument, or an “enum” type (discussed shortly) and selects among a number of alternative, constant case branches:[8]

    switch ( expression )
    {
        case constantExpression :
            statement;
        [ case constantExpression :statement;  ]
        ...
        [ default :
            statement;  ]
    }

The case expression for each branch must evaluate to a different constant integer or string value at compile time. Strings are compared using the String equals() method, which we’ll discuss in more detail in Chapter 10. An optional default case can be specified to catch unmatched conditions. When executed, the switch simply finds the branch matching its conditional expression (or the default branch) and executes the corresponding statement. But that’s not the end of the story. Perhaps counterintuitively, the switch statement then continues executing branches after the matched branch until it hits the end of the switch or a special statement called break. Here are a couple of examples:

    int value = 2;

    switch( value ) {
        case 1:
            System.out.println( 1 );
        case 2:
            System.out.println( 2 );
        case 3:
            System.out.println( 3 );
    }

    // prints 2, 3!

Using break to terminate each branch is more common:

    int retValue = checkStatus();

    switch ( retVal )
    {
        case MyClass.GOOD :
            // something good
            break;
        case MyClass.BAD :
            // something bad
            break;
        default :
            // neither one
            break;
    }

In this example, only one branch—GOOD, BAD, or the default—is executed. The “fall through” behavior of the switch is justified when you want to cover several possible case values with the same statement without resorting to a bunch of if/else statements:

    int value = getSize();

    switch( value ) {
        case MINISCULE:
        case TEENYWEENIE:
        case SMALL:
            System.out.println("Small" );
            break;
        case MEDIUM:
            System.out.println("Medium" );
            break;
        case LARGE:
        case EXTRALARGE:
            System.out.println("Large" );
            break;
    }

This example effectively groups the six possible values into three cases.

The Java break statement and its friend continue can also be used to cut short a loop or conditional statement by jumping out of it. A break causes Java to stop the current block statement and resume execution after it. In the following example, the while loop goes on endlessly until the condition() method returns true, triggering a break statement that stops the loop and proceeds at the point marked “after while.”

    while( true ) {
        if ( condition() )
             break;
    }
    // after while

A continue statement causes for and while loops to move on to their next iteration by returning to the point where they check their condition. The following example prints the numbers 0 through 99, skipping number 33.

    for( int i=0; i < 100; i++ ) {
        if ( i == 33 )
            continue;
        System.out.println( i );
    }

The break and continue statements look like those in the C language, but Java’s forms have the additional ability to take a label as an argument and jump out multiple levels to the scope of the labeled point in the code. This usage is not very common in day-to-day Java coding, but may be important in special cases. Here is an outline:

    labelOne:
        while ( condition ) {
            ...
            labelTwo:
                while ( condition ) {
                    ...

                    // break or continue point
                }
            // after labelTwo
        }
    // after labelOne

Enclosing statements, such as code blocks, conditionals, and loops, can be labeled with identifiers like labelOne and labelTwo. In this example, a break or continue without argument at the indicated position has the same effect as the earlier examples. A break causes processing to resume at the point labeled “after labelTwo”; a continue immediately causes the labelTwo loop to return to its condition test.

The statement break labelTwo at the indicated point has the same effect as an ordinary break, but break labelOne breaks both levels and resumes at the point labeled “after labelOne.” Similarly, continue labelTwo serves as a normal continue, but continue labelOne returns to the test of the labelOne loop. Multilevel break and continue statements remove the main justification for the evil goto statement in C/C++.

There are a few Java statements we aren’t going to discuss right now. The try , catch, and finally statements are used in exception handling, as we’ll discuss later in this chapter. The synchronized statement in Java is used to coordinate access to statements among multiple threads of execution; see Chapter 9 for a discussion of thread synchronization.

An expression produces a result, or value, when it is evaluated. The value of an expression can be a numeric type, as in an arithmetic expression; a reference type, as in an object allocation; or the special type, void, which is the declared type of a method that doesn’t return a value. In the last case, the expression is evaluated only for its side effects; that is, the work it does aside from producing a value. The type of an expression is known at compile time. The value produced at runtime is either of this type or in the case of a reference type, a compatible (assignable) subtype.

Java supports almost all standard operators from the C language. These operators also have the same precedence in Java as they do in C, as shown in Table 4-3.

We should also note that the percent (%) operator is not strictly a modulo, but a remainder, and can have a negative value.

Java also adds some new operators. As we’ve seen, the + operator can be used with String values to perform string concatenation. Because all integral types in Java are signed values, the >> operator can be used to perform a right-arithmetic-shift operation with sign extension. The >>> operator treats the operand as an unsigned number and performs a right-arithmetic-shift with no sign extension. The new operator is used to create objects; we will discuss it in detail shortly.

Objects in Java are allocated with the new operator:

    Object o = new Object();

The argument to new is the constructor for the class. The constructor is a method that always has the same name as the class. The constructor specifies any required parameters to create an instance of the object. The value of the new expression is a reference of the type of the created object. Objects always have one or more constructors, though they may not always be accessible to you.

We look at object creation in detail in Chapter 5. For now, just note that object creation is a type of expression and that the result is an object reference. A minor oddity is that the binding of new is “tighter” than that of the dot (.) selector. So you can create a new object and invoke a method in it without assigning the object to a reference type variable if you have some reason to:

    int hours = new Date().getHours();

The Date class is a utility class that represents the current time. Here we create a new instance of Date with the new operator and call its getHours() method to retrieve the current hour as an integer value. The Date object reference lives long enough to service the method call and is then cut loose and garbage-collected at some point in the future (see Chapter 5 for details about garbage collection).

Calling methods in object references in this way is, again, a matter of style. It would certainly be clearer to allocate an intermediate variable of type Date to hold the new object and then call its getHours() method. However, combining operations like this is common.



[8] Strings in switch statements were added in Java 7.