Friday, February 24, 2023

Quiz yourself: How does a Java finally block handle an exception?

Quiz yourself, Oracle Java, Java Tutorial and Materials, Oracle Java Prep, Java Preparation, Java Learning, Java Certification, Java Guides, Java Learning, Java Guides

You’ll want to know the difference between abrupt completion and normal completion.


Given these two exception classes

class BatteryException extends Exception { }
class FuelException extends Exception { }

And the following Car class

public class Car {
  public String checkBattery() throws BatteryException {
    // implementation
  }
  public String checkFuel() throws FuelException {
    // implementation
  }
  public String start() {
    try {
      checkBattery();
      checkFuel();
    } catch (BatteryException be) {
      return "BadBattery";
    } finally {
      return "";
    }
  }
}

Which statement is correct about the start() method? Choose one.

A. It may return BadBattery or an empty string.
B. It can only return an empty string.
C. It may throw FuelException.
D. It will cause a compilation error because FuelException is not handled.

Answer. This question investigates a less-frequently used behavior of a finally block.

Looking at the code, notice that there are two domain-specific exceptions related to the battery and the fuel. They’re direct subtypes of Exception, which means that they are checked exceptions. If such an exception might be thrown by a method, it must be declared in the throws clause of that method.

The usual way to handle an exception inside a method is to use a try-catch structure and provide a catch block that names the exception, or a parent type of that exception. In this code, there’s a catch block for the BatteryException but not for the FuelException. Given that the method does not declare throws FuelException, you might expect the compiler to refuse to compile the code.

However, if you think a bit deeper on this, you should realize that the finally block executes return "". This does exactly what it says: No matter how the code reaches the finally block, the result is that the method will return an empty string. No exceptions will be thrown; indeed, any FuelException that might arise will simply be abandoned. In other words, because FuelException is never thrown by the method, it’s not necessary to declare it in a throws clause.

The above examination shows that options C and D are both incorrect, because no FuelException is possible, and the code does not fail to compile due to a missing throws clause.

Digging into this logic a little further, if the code executes return "Bad battery" from inside the try block, it must execute the finally block before control is ultimately passed to the caller. This too causes the method to return the empty string from inside the finally block. This tells you that option A is also incorrect. Further, when you combine this with the earlier discussion, you should see that the code always returns an empty string; therefore, option B is correct.

It might be of interest to follow up on the qualitative descriptions above with some details from the Java Language Specification. First, you need to understand the meaning of the phrase abrupt completion, which is the topic of section 14.1. This section might be paraphrased as follows: If a region of code runs to its end, it completes normally. If, by contrast, it jumps out of that region without completing all the steps, it completes abruptly.

Note that this doesn’t imply (nor does it exclude) exceptions. In particular, a return statement constitutes an abrupt completion of a method, where running off the end—that is, reaching the closing curly brace of the method—is normal completion. (You should read the specification for a formal, and more complete, description.)

With that in mind, let’s continue looking in the specification and now focus on the behavior of catch and finally. In section 14.20.2 you’ll find the following text:

If the catch block completes normally, then the finally block is executed. Then there is a choice:

◉ If the finally block completes normally, then the try statement completes normally.
◉ If the finally block completes abruptly for any reason, then the try statement completes abruptly for the same reason.

If the catch block completes abruptly for reason R, then the finally block is executed. Then there is a choice:

◉ If the finally block completes normally, then the try statement completes abruptly for reason R.
◉ If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).

These paragraphs explain that abrupt completion of the finally block means that the try construct completes abruptly for the same reason. In effect, this supersedes any previous mode of completion and any previous reason for abrupt completion.

Note that execution of a return statement constitutes abrupt completion, as explained in the following sentence from section 14.17:

It can be seen, then, that a return statement always completes abruptly.

You know that the return "" in the finally block is always executed because there are no paths through the method that do not enter the try construct. Putting this together with the specification excerpts above, you can see that the only possible result of executing the method is abrupt completion, which returns an empty string.

Conclusion. The correct answer is option B.

Source: oracle.com

Related Posts

0 comments:

Post a Comment