5 Control Flow, Exception Handling, and Assertions

5.1

(d)

The program will display the letter b when run. The second if statement is evaluated since the boolean expression of the first if statement is true. The else clause belongs to the second if statement. Since the boolean expression of the second if statement is false, the if block is skipped and the else clause is executed.

5.2

(a), (b), and (e)

The conditional expression of an if statement can have any subexpressions, including method calls, as long as the whole expression evaluates to a value of type boolean. The expression (a = b) does not compare the variables a and b, but assigns the value of b to the variable a. The result of the expression is the value being assigned. Since a and b are boolean variables, the value returned by the expression is also boolean. This allows the expression to be used as the condition for an if statement. An if statement must always have an if block, but the else clause is optional. The expression if (false) ; else ; is legal. In this case, both the if block and the else block are simply the empty statement.

5.3

(f)

There is nothing wrong with the code. The case and default labels do not have to be specified in any specific order. The use of the break statement is not mandatory, and without it the control flow will simply fall through the labels of the switch statement.

5.4

(a)

The type of the switch expression must be either byte, char, short, or int. This excludes (b) and (e). The type of the case labels must be assignable to the type of the switch expression. This excludes (c) and (d).

5.5

(e)

The loop body is executed twice and the program will print 3. The first time the loop is executed, the variable i changes from 1 to 2 and the variable b changes from false to true. Then the loop condition is evaluated. Since b is true, the loop body is executed again. This time the variable i changes from 2 to 3 and the variable b changes from true to false. The loop condition is now evaluated again. Since b is now false, the loop terminates and the current value of i is printed.

5.6

(b) and (e)

Both the first and the second number printed will be 10. Both the loop body and the increment expression will be executed exactly 10 times. Each execution of the loop body will be directly followed by an execution of the increment expression. Afterwards, the condition j<10 is evaluated to see whether the loop body should be executed again.

5.7

(c)

Only (c) contains a valid for loop. The initializer in a for statement can contain either declarations or a list of expression statements, but not both as attempted in (a). The loop condition must be of type boolean. (b) tries to use an assignment of an int value (notice the use of = rather than ==) as a loop condition and is, therefore, not valid. The loop condition in the for loop (d) tries to use the uninitialized variable i, and the for loop in (e) is simply syntactically invalid.

5.8

(f)

The code will compile without error, but will never terminate when run. All the sections in the for header are optional and can be omitted (but not the semicolons).

An omitted loop condition is interpreted as being true. Thus, a for loop with an omitted loop condition will never terminate, unless a break statement is encountered in the loop body. The program will enter an infinite loop at (4).

5.9

(b), (d), and (e)

The loop condition in a while statement is not optional. It is not possible to break out of the if statement in (c). Notice that if the if statement had been placed within a labeled block, a switch statement, or a loop, the usage of break would be valid.

5.10

(a) and (d)

"i=1, j=0" and "i=2, j=1" are part of the output. The variable i iterates through the values 0, 1, and 2 in the outer loop, while j toggles between the values 0 and 1 in the inner loop. If the values of i and j are equal, the printing of the values is skipped and the execution continues with the next iteration of the outer loop. The following can be deduced when the program is run: variables i and j are both 0 and the execution continues with the next iteration of the outer loop. "i=1, j=0" is printed and the next iteration of the inner loop starts. Variables i and j are both 1 and the execution continues with the next iteration of the outer loop. "i=2, j=0" is printed and the next iteration of the inner loop starts. "i=2, j=1" is printed, j is incremented, j < 2 fails, and the inner loop ends. Variable i is incremented, i < 3 fails, and the outer loop ends.

5.11

(b)

The code will fail to compile, since the conditional expression of the if statement is not of type boolean. The conditional expression of an if statement must be of type boolean. The variable i is of type int. There is no conversion between boolean and other primitive types.

5.12

(d)

Implementation (4) will correctly return the largest value. The if statement does not return any value and, therefore, cannot be used as in implementations (1) and (2). Implementation (3) is invalid since neither the switch expression nor the case label values can be of type boolean.

5.13

(c)

As it stands, the program will compile correctly and will print "3, 2" when run. If the break statement is replaced with a continue statement, the loop will perform all four iterations and will print "4, 3". If the break statement is replaced with a return statement, the whole method will end when i equals 2, before anything is printed. If the break statement is simply removed, leaving the empty statement (;), the loop will complete all four iterations and will print "4, 4".

5.14

(a) and (c)

The block construct {} is a compound statement. The compound statement can contain zero or more arbitrary statements. Thus, {{}} is a legal compound statement, containing one statement that is also a compound statement, containing no statement. The block { continue; } by itself is not valid, since the continue statement cannot be used outside the context of a loop. (c) is a valid example of breaking out of a labeled block. (d) is not valid for the same reasons (b) was not valid. The statement at (e) is not true, since the break statement can also be used to break out of labeled blocks, as illustrated by (c).

5.15

(d)

The program will only print 1, 4, and 5, in that order. The expression 5/k will throw an ArithmeticExecption, since k equals 0. Control is transferred to the first catch block, since it is the first block that can handle arithmetic exceptions. This exception handler simply prints 1. The exception has now been caught and normal execution can resume. Before leaving the try statement, the finally block is executed. This block prints 4. The last statement of the main() method prints 5.

5.16

(b) and (e)

If run with one argument, the program will print the given argument followed by "The end". The finally block will always be executed, no matter how control leaves the try block.

5.17

(d)

The program will compile without error, but will throw a NullPointerException when run. The throw statement can only throw Throwable objects. A NullPointerException will be thrown if the expression of the throw statement results in a null reference.

5.18

(c) and (d)

Normal execution will only resume if the exception is caught by the method. The uncaught exception will propagate up the runtime stack until some method handles it. An overriding method need only declare that it can throw a subset of the checked exceptions the overridden method can throw. The main() method can declare that it throws checked exceptions just like any other method. The finally block will always be executed, no matter how control leaves the try block.

5.19

(b)

The program will print 1 and 4, in that order. An InterruptedException is handled in the first catch block. Inside this block a new RuntimeException is thrown. This exception was not thrown inside the try block and will not be handled by the catch blocks, but will be sent to the caller of the main() method. Before this happens, the finally block is executed. The code printing 5 is never reached, since the runtimeException remains uncaught after the execution of the finally block.

5.20

(a)

The program will print 2 and throw an InterruptedException. An InterruptedException is thrown in the try block. There is no catch block to handle the exception, so it will be sent to the caller of the main() method, that is, to the default exception handler. Before this happens, the finally block is executed. The code printing 3 is never reached.

5.21

(b)

The only thing that is wrong with the code is the ordering of the catch and finally blocks. If present, the finally block must always appear last in a try-catch-finally construct.

5.22

(a)

Overriding methods can specify all, none, or a subset of the checked exceptions the overridden method declares in its throws clause. The InterruptedException is the only checked exception specified in the throws clause of the overridden method. The overriding method f() need not specify any checked exception from the throws clause of the overridden method.

5.23

(c)

The overriding f() method in MyClass is not permitted to throw the checked InterruptedException, since the f() method in class A does not throw this exception. To avoid compilation errors, either the overriding f() method must not throw an InterruptedException or the overridden f() method must declare that it can throw an InterruptedException.

5.24

(c) and (d)

Statements (c) and (d) will throw an AssertionError because the first expression is false. Statement (c) will report true as the error message, while (d) will report false as the error message.

5.25

(b) and (d)

-ea (enable assertions) is a valid runtime option, not -ae. -source 1.4 is a compile time option. -dsa (disable system assertions) is a valid runtime option, not -dea.

5.26

(e)

The class of exceptions thrown by assertion statements is always AssertionError.

5.27

(c)

Assertions can be enabled or disabled at runtime, but the assert statements are always compiled into bytecode.

5.28

(a) and (b)

Statement (a) will cause the assert statement to throw an AssertionError since (-50 > -50) evaluates to false. Statement (b) will cause the expression 100/value to throw an ArithmeticException since an integer division by zero is attempted.

5.29

(a) and (d)

Option (a) enables assertions for all non-system classes, while (d) enables assertions for all classes in the package org and its subpackages. Options (b), (c), and (f) try to enable assertions in specifically named classes: Bottle, org.example, and org.example.ttp. Option (e) is not a valid runtime option.

5.30

(c)

The assert statement correctly asserts that 150 is greater than 100 and less than 200.

5.31

(b) and (d)

The AssertionError class, like all other error classes, is not a checked exception, and need not be declared in a throws clause. After an AssertionError is thrown, it is propagated exactly the same way as other exceptions, and can be caught by a try-catch construct.

5.32

(d)

The class Error is the direct superclass of AssertionError.

5.33

(c) and (d)

The command line enables assertions for all non-system classes, except for those in the com package or one of its subpackages. Assertions are not enabled for the system classes in (b) and (e).