10.12 Other Control Structures

Java has other ways to combine groups of statements besides if. One is the switch statement, implemented using the tableswitch and lookupswitch instructions described in section 5.4. Java also has looping constructs like for and while, discussed in the forthcoming sections.

10.12.1 Loops

The Java language has three looping constructs: for, while, and do/while:

 // Loop 100 times. Each time, the variable i takes on a // different value from 0 to 99. for(int i = 0; i < 100; i++)    System.out.println("f(" + i + ") = " + f(i)); // Loop forever, printing out "Hello, world" // an infinite number of times while(true)    System.out.println("Hello, world"); // Loop until the character c is EOF char c; do {    c = nextToken();        // Get the next token    processToken(c);        // Process the token } while(c != EOF); 

In each of these three statements, a substatement is guarded by an expression whose compile-time type is boolean. The statement is performed over and over again. Before the statement is executed, the guard is evaluated. If the expression is true, then the statement is executed. Otherwise, the loop is terminated, and control continues with the next statement after the loop.

In a while loop, the expression is evaluated before the statement has been executed even once, but the do/while loop executes the statement once before testing the loop for the first time. The do/while loop is used when the statement must be evaluated at least once.

A for loop is a lot like a while loop, with a little extra syntactic sugar. Many loops use a special variable called the index variable. The index variable is initialized before the loop, then incremented each time the statement is executed. This continues as long as the expression evaluates to true.

The for loop in the example can be rewritten as a while loop like this:

 {    int i = 0;    while(i < 10)    {        System.out.println("f(" + i + ") = " + f(i));        i++;    } } 

The extra braces around the whole thing ensure that the variable declaration of i does not conflict with other definitions of i later in the same method. (See the discussion of scoping in section 10.4.) The variable i is local to the loop.

10.12.2 Compiling Control Constructs

Compiling a while statement is a lot like compiling an if statement, as discussed in section 10.11. If the test is true, the statement is executed, followed by a goto back to the test. If the test is false, control skips to the next statement. For example,

 while((c = nextToken()) != EOF) {    processToken(c); } 

The expression (c = nextToken()) != EOF depends on the fact that the result of an assignment is the value assigned, as shown in section 10.5.3. This expression calls nextToken() and assigns the result to c. The value of the assignment expression as a whole is the same as the value assigned to c. The value is then compared against EOF. This comparison is used as the guard expression of the loop.

Compiling this code is similar to compiling an if. If the expression is true, then do a backwards goto to the beginning of the loop. If not, break out of the loop. The example statement compiles to

 .var 1 is c I loop:                                ; Loop starts here    invokestatic Lexer/nextToken()I   ; Get the next token    dup                               ; Dup it so that the value                                      ; remains on the stack                                      ; after assignment    istore_1                          ; Assign it to c    getstatic Lexer/EOF I             ; Get the EOF value    if_icmpeq break_loop              ; Compare and break the loop                                      ; if EOF is found    ; This is the body of the loop:    iload_1                               ; Push c    invokestatic Lexer/processToken(I)V   ; Process the token    goto loop            ; And loop again break_loop:             ; Next statement goes here 

The do/while loop is similar to a while loop, except that the test is moved from the beginning of the loop to the end. Consider the same code written as a do/while loop instead of a while loop:

 // Assume that c has some initial value do {    processToken(c); } while((c = nextToken()) != EOF) 

The meaning of this loop is the same as that of the while loop, except that processToken is called once before entering the loop. This is fine as long as c has some initial value.

The resulting bytecodes are similar to the while loop shown earlier, except that the code has been rearranged to ensure that the body is executed at least once:

 .var 1 is c I loop:                                       ; Loop starts here    ; The body of the loop now goes here at the beginning    iload_1                                  ; Push c    invokestatic Lexer/processToken(I)V      ; Process the token    ; Perform the test, and break the loop if the test fails    invokestatic Lexer/nextToken()I          ; Get the next token    dup                                      ; Dup it so that the                                             ; value remains on                                             ; the stack    istore_1                                 ; Assign it to c    getstatic Lexer/EOF I                    ; Get the EOF value    if_icmpeq break_loop            ; Compare and break the loop                                    ; if EOF is found    goto loop                       ; Loop again if not EOF break_loop:                        ; Next statement goes here 

10.12.3 Compound Statements and Verification

Statements can be combined to make a block by concatenating the code for them together. Since each statement by itself has no effect on the stack, there is no effect from putting one after the other.

Consider this method, which takes an array of strings that represent numbers and computes the sum and product of all those numbers:

 public static void main(String args[]) {    int sum = 0;    int product = 1;    for(int i = 0; i < args.length; i++)    {        int n = Integer.parseInt(args[i]);        sum += n;        product *= n;    }    System.out.println("sum = " + sum);    System.out.println("product = " + product); } 

The value args is found in variable 0, since it's the first (and only) argument to the static method. The assignment of variables to slots is

 .var 0 is args [Ljava/lang/String; .var 1 is sum I .var 2 is product I .var 3 is i I .var 4 is n I 

The loop body

 int n = Integer.parseInt(args[i]); sum += n; product *= n; 

compiles into

 ; int n = Integer.parseInt(args[i]) aload_0               ; Push args iload_3               ; Push i iaload                ; Get a[i]                       ; Compute parseInt(a[i]) invokestatic java/lang/Integer/parseInt (Ljava/lang/String;)I istore 4                       ; Store the result in n ; The stack is now empty ; sum += n; iload_1                        ; Push sum iload 4                        ; Push n iadd                           ; Compute sum+n istore_1                       ; Store the result in sum ; The stack is now empty ; product *= n; iload_2                        ; Push product iload 4                        ; Push n imul                           ; Compute product*n istore_2                       ; Store the result in product ; The stack is now empty 

The code for each statement has no effect on the stack, so when the statements are put together the net effect is zero. Together, they act like a single statement. This is important when you want to use the statement as the body of a loop construct.

The for statement can be compiled into

 iconst_0             ; Inititalize i to 0 istore_3    ; The stack is now empty loop:                ; The beginning of the for loop    iload_3           ; See if i < args.length    aload_0    arraylength    ifge break        ; If i >= args.length, break    ;; Insert the code for the big substatement here    ; The stack is now empty    iinc 3 1          ; Increment i by 1    goto loop         ; And loop again break: 

When the verification algorithm looks at this code, one thing it checks is whether or not the stack is always the same each time the code hits loop. Because the stack is empty each time, the verification algorithm approves this code.



Programming for the Java Virtual Machine
Programming for the Javaв„ў Virtual Machine
ISBN: 0201309726
EAN: 2147483647
Year: 1998
Pages: 158
Authors: Joshua Engel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net