Java provides six language constructs for transferring control in a program:
break
continue
return
try-catch-finally
throw
assert
This section discusses the first three statements, and the remaining statements are discussed in subsequent sections.
Note that Java does not have a goto statement, although goto is a reserved word.
A statement may have a label.
<label> : <statement>
A label is any valid identifier and it always immediately proceeds the statement. Label names exist in their own name space, so that they do not conflict with names of packages, classes, interfaces, methods, fields, and local variables. The scope of a label is the statement prefixed by the label, meaning that it cannot be redeclared as a label inside the labeled statement?analogous to the scope of local variables.
L1: if (i > 0) { L1: System.out.println(i); // (1) Not OK. Label redeclared. } L1: while (i < 0) { // (2) OK. L2: System.out.println(i); } L1: { // (3) OK. Labeled block. int j = 10; System.out.println(j); } L1: try { // (4) OK. Labeled try-catch-finally block. int j = 10, k = 0; L2: System.out.println(j/k); } catch (ArithmeticException ae) { L3: ae.printStackTrace(); } finally { L4: System.out.println("Finally done."); }
A statement can have multiple labels:
LabelA: LabelB: System.out.println("Mutliple labels. Use judiciously.");
A declaration statement cannot have a label:
L0: int i = 0; // Compile time error.
A labeled statement is executed like it was non-labeled, unless it contains the break or continue statements. This is discussed in the next two subsections.
The break statement comes in two forms: the unlabeled and the labeled form.
break; // the unlabeled form
break <label>; // the labeled form
The unlabeled break statement terminates loops (for, while, do-while) and switch statements which contain the break statement, and transfers control out of the current context (i.e., the closest enclosing block). The rest of the statement body is skipped, terminating the enclosing statement, with execution continuing after this statement.
In Example 5.4, the break statement at (1) is used to terminate a for loop. Control is transferred to (2) when the value of i is equal to 4 at (1), skipping the rest of the loop body and terminating the loop.
Example 5.4 also shows that the unlabeled break statement only terminates the innermost loop or switch statement that contains the break statement. The break statement at (3) terminates the inner for loop when j is equal to 2, and execution continues in the outer switch statement at (4) after the for loop.
class BreakOut { public static void main(String[] args) { for (int i = 1; i <= 5; ++i) { if (i == 4) break; // (1) Terminate loop. Control to (2). // Rest of loop body skipped when i gets the value 4. System.out.println(i + "\t" + Math.sqrt(i)); } // end for // (2) Continue here. int n = 2; switch (n) { case 1: System.out.println(n); break; case 2: System.out.println("Inner for loop: "); for (int j = 0; j < n; j++) if (j == 2) break; // (3) Terminate loop. Control to (4). else System.out.println(j); default: System.out.println("default: " + n); // (4) Continue here. } } }
Output from the program:
1 1.0 2 1.4142135623730951 3 1.7320508075688772 Inner for loop: 0 1 default: 2
A labeled break statement can be used to terminate any labeled statement that contains the break statement. Control is then transferred to the statement following the enclosing labeled statement. In the case of a labeled block, the rest of the block is skipped and execution continues with the statement following the block:
out: { // (1) Labeled block // ... if (j == 10) break out; // (2) Terminate block. Control to (3). System.out.println(j); // Rest of the block not executed if j == 10. // ... } // (3) Continue here.
In Example 5.5, the program continues to add the elements below the diagonal of a square matrix until the sum is greater than 10. Two nested for loops are defined at (1) and (2). The outer loop is labeled outer at (1). The unlabeled break statement at (3) transfers control to (5) when it is executed, that is, it terminates the inner loop and control is transferred to the statement after the inner loop. The labeled break statement at (4) transfers control to (6) when it is executed (i.e., it terminates both the inner and the outer loop, transferring control to the statement after the loop labeled outer).
class LabeledBreakOut { public static void main(String[] args) { int[][] squareMatrix = {{4, 3, 5}, {2, 1, 6}, {9, 7, 8}}; int sum = 0; outer: // label for (int i = 0; i < squareMatrix.length; ++i){ // (1) for (int j = 0; j < squareMatrix[i].length; ++j) { // (2) if (j == i) break; // (3) Terminate this loop. // Control to (5). System.out.println("Element[" + i + ", " + j + "]: " + squareMatrix[i][j]); sum += squareMatrix[i][j]; if (sum > 10) break outer;// (4) Terminate both loops. // Control to (6). } // end inner loop // (5) Continue with outer loop. } // end outer loop // (6) Continue here. System.out.println("sum: " + sum); } }
Output from the program:
Element[1, 0]: 2 Element[2, 0]: 9 sum: 11
Like the break statement, the continue statement also comes in two forms: the unlabeled and the labeled form.
continue; // the unlabeled form
continue <label>; // the labeled form
The continue statement can only be used in a for, while, or do-while loop to prematurely stop the current iteration of the loop body and proceed with the next iteration, if possible. In the case of the while and do-while loops, the rest of the loop body is skipped, that is, stopping the current iteration, with execution continuing with the <loop condition>. In the case of the for loop, the rest of the loop body is skipped, with execution continuing with the <increment expression>.
In Example 5.6, an unlabeled continue statement is used to skip an iteration in a for loop. Control is transferred to (2) when the value of i is equal to 4 at (1), skipping the rest of the loop body and continuing with the <increment expression> in the for statement.
class Skip { public static void main(String[] args) { for (int i = 1; i <= 5; ++i) { if (i == 4) continue; // (1) Control to (2). // Rest of loop body skipped when i has the value 4. System.out.println(i + "\t" + Math.sqrt(i)); // (2). Continue with increment expression. } // end for } }
Output from the program:
1 1.0 2 1.4142135623730951 3 1.7320508075688772 5 2.23606797749979
A labeled continue statement must occur within a labeled loop that has the same label. Execution of the labeled continue statement then transfers control to the end of that enclosing labeled loop. In Example 5.7, the unlabeled continue statement at (3) transfers control to (5) when it is executed; that is, the rest of the loop body is skipped and execution continues with the next iteration of the inner loop. The labeled continue statement at (4) transfers control to (6) when it is executed (i.e., it terminates the inner loop but execution continues with the next iteration of the loop labeled outer). It is instructive to compare the output from Example 5.5 (labeled break) and Example 5.7 (labeled continue).
class LabeledSkip { public static void main(String[] args) { int[][] squareMatrix = {{4, 3, 5}, {2, 1, 6}, {9, 7, 8}}; int sum = 0; outer: // label for (int i = 0; i < squareMatrix.length; ++i){ // (1) for (int j = 0; j < squareMatrix[i].length; ++j) { // (2) if (j == i) continue; // (3) Control to (5). System.out.println("Element[" + i + ", " + j + "]: " + squareMatrix[i][j]); sum += squareMatrix[i][j]; if (sum > 10) continue outer; // (4) Control to (6). // (5) Continue with inner loop. } // end inner loop // (6) Continue with outer loop. } // end outer loop System.out.println("sum: " + sum); } }
Output from the program:
Element[0, 1]: 3 Element[0, 2]: 5 Element[1, 0]: 2 Element[1, 2]: 6 Element[2, 0]: 9 sum: 25
The return statement is used to stop execution of a method and transfer control back to the calling code (a.k.a. the caller). The usage of the two forms of the return statement is dictated by whether it is used in a void or a non-void method. The first form does not return any value to the calling code, but the second form does. Note that the keyword void does not represent any type.
The <expression> must evaluate to a primitive value or a reference value, and its type must be assignable to the return type in the method prototype (see Sections 3.4 and 6.6). As can be seen from Table 5.1, non-void methods must specify a return value using the return statement. A void method need not have a return statement ?in which case control normally returns to the caller after the last statement in the method's body has been executed. The first form of the return statement can also be used in constructors, as these also do not return a value. Example 5.8 illustrates the usage of the return statement summarized in Table 5.1.
Form of return Statement | In void Method | In Non-void Method |
---|---|---|
return; | optional | not allowed |
return <expression>; | not allowed | mandatory |
public class ReturnDemo { public static void main (String[] args) { // (1) void method can use return. if (args.length == 0) return; output(checkValue(args.length)); } static void output(int value) { // (2) void method need not use return. System.out.println(value); return 'a'; // Not OK. Cannot return a value. } static int checkValue(int i) { // (3) non-void method must return a value. if (i > 3) return i; // OK. else return 2.0; // Not OK. double not assignable to int. } }