The finally Clause
If the try block executes, then the finally clause is guaranteed to be executed, regardless of whether any catch clause was executed, barring the two special cases (JVM crashes or the System.exit() method is called). Since the finally clause is always executed before control transfers to its final destination, the finally clause can be used to specify any clean-up code (e.g., to free resources such as files and network connections). However, the try-with-resources statement provides a better solution for handling resources, and eliminates the use of the finally clause in many cases (p. 407).
A try-finally construct can be used to control the interplay between two actions that must be executed in the correct order, possibly with other intervening actions. In the code below, the operation in the calculateAverage() method (called at (2)) is dependent on the success of the sumNumbers() method (called at (1)). The if statement at (2) checks the value of the sum variable before calling the calculateAverage() method:
int sum = 0;
try {
sum = sumNumbers(); // (1)
// other actions
} finally {
if (sum > 0) calculateAverage(); // (2)
}
This code guarantees that if the try block is entered, the sumNumbers() method will be executed first, and later the calculateAverage() method will be executed in the finally clause, regardless of how execution proceeds in the try block. We can, if desired, include any catch clauses to handle any exceptions.
If the finally clause neither throws an exception nor executes a control transfer statement like a return or a labeled break, the execution of the try block or any catch clause determines how execution proceeds after the finally clause (Figure 7.4, p. 376).
- If no exception is thrown during execution of the try block or the exception has been handled in a catch clause, normal execution continues after the finally clause.
- If there is any uncaught exception (either because no matching catch clause was found or because the catch clause threw an exception), the method completes abruptly and the exception is propagated after the execution of the finally clause.
The output of Example 7.4 shows that the finally clause at (4) is executed, regardless of whether an exception is thrown in the try block at (2). If an Arithmetic-Exception is thrown, it is caught and handled by the catch clause at (3). After the execution of the finally clause at (4), normal execution continues at (5).
Example 7.4 The try-catch-finally Construct
public class Average4 {
public static void main(String[] args) {
printAverage(100, 20); // (1)
System.out.println(“Exit main().”);
}
public static void printAverage(int totalSum, int totalCount) {
try { // (2)
int average = computeAverage(totalSum, totalCount);
System.out.println(“Average = ” +
totalSum + ” / ” + totalCount + ” = ” + average);
} catch (ArithmeticException ae) { // (3)
ae.printStackTrace();
System.out.println(“Exception handled in printAverage().”);
} finally { // (4)
System.out.println(“Finally done.”);
}
System.out.println(“Exit printAverage().”); // (5)
}
public static int computeAverage(int sum, int count) {
System.out.println(“Computing average.”);
return sum/count;
}
}
Output from the program, with the call printAverage(100, 20) at (1):
Computing average.
Average = 100 / 20 = 5
Finally done.
Exit printAverage().
Exit main().
Output from the program, with the call printAverage(100, 0) at (1):
Computing average.
java.lang.ArithmeticException: / by zero
at Average4.computeAverage(Average4.java:24)
at Average4.printAverage(Average4.java:10)
at Average4.main(Average4.java:4)
Exception handled in printAverage().
Finally done.
Exit printAverage().
Exit main().
On exiting from the finally clause, if there is any uncaught exception, the method completes abruptly and the exception is propagated as explained earlier. This is illustrated in Example 7.5. The method printAverage() is aborted after the finally clause at (3) has been executed, as the ArithmeticException thrown at (4) is not caught by any method. In this case, the exception is handled by the default exception handler. Notice the difference in the output from Example 7.4 and Example 7.5.
Example 7.5 The try-finally Construct
public class Average5 {
public static void main(String[] args) {
printAverage(100, 0); // (1)
System.out.println(“Exit main().”);
}
public static void printAverage(int totalSum, int totalCount) {
try { // (2)
int average = computeAverage(totalSum, totalCount);
System.out.println(“Average = ” +
totalSum + ” / ” + totalCount + ” = ” + average);
} finally { // (3)
System.out.println(“Finally done.”);
}
System.out.println(“Exit printAverage().”);
}
public static int computeAverage(int sum, int count) {
System.out.println(“Computing average.”);
return sum/count; // (4)
}
}
Output from the program:
Computing average.
Finally done.
Exception in thread “main” java.lang.ArithmeticException: / by zero
at Average5.computeAverage(Average5.java:21)
at Average5.printAverage(Average5.java:10)
at Average5.main(Average5.java:4)
If the finally clause executes a control transfer statement, such as a return or a labeled break, this control transfer statement determines how the execution will proceed—regardless of how the try block or any catch clause was executed. In particular, a value returned by a return statement in the finally clause will supercede any value returned by a return statement in the try block or a catch clause.
Example 7.6 shows how the execution of a control transfer statement such as a return in the finally clause affects the program execution. The first output from the program shows that the average is computed but the value returned is from the return statement at (3) in the finally clause, not from the return statement at (2) in the try block. The second output shows that the ArithmeticException thrown in the computeAverage() method and propagated to the printAverage() method is suppressed by the return statement in the finally clause. Normal execution continues after the return statement at (3), with the value 0 being returned from the printAverage() method.
If the finally clause throws an exception, this exception is propagated with all its ramifications—regardless of how the try block or any catch clause was executed. In particular, the new exception overrules any previously uncaught exception (p. 415).
Example 7.6 The finally Clause and the return Statement
public class Average6 {
public static void main(String[] args) {
System.out.println(“Value: ” + printAverage(100, 20)); // (1)
System.out.println(“Exit main().”);
}
public static int printAverage(int totalSum, int totalCount) {
int average = 0;
try {
average = computeAverage(totalSum, totalCount);
System.out.println(“Average = ” +
totalSum + ” / ” + totalCount + ” = ” + average);
return average; // (2)
} finally {
System.out.println(“Finally done.”);
return average*2; // (3)
}
}
public static int computeAverage(int sum, int count) {
System.out.println(“Computing average.”);
return sum/count;
}
}
Output from the program, with call printAverage(100, 20) at (1):
Computing average.
Average = 100 / 20 = 5
Finally done.
Value: 10
Exit main().
Output from the program, with call printAverage(100, 0) at (1):
Computing average.
Finally done.
Value: 0
Exit main().