Throwing and Catching Exceptions
|Navigate Exceptions topic: )|
Language compilers are adept at pointing out most of the erroneous code in a program, however there are some errors that only become apparent when the program is executed. Consider the code listing 6.1; here, the program defines a method divide that does a simple division operation taking two integers as parameter arguments and returning the result of their division. It can safely be assumed that when the divide(4, 2) statement is called, it would return the number 2. However, consider the next statement, where the program relies upon the provided command line arguments to generate a division operation. What if the user provides the number zero (0) as the second argument? We all know that division by zero is impossible, but the compiler couldn't possibly have anticipated the user providing zero as an argument.
Such exceptional code that results in erroneous interpretations at program runtime usually results in errors that are called exceptions in Java. When the Java interpreter encounters an exceptional code, it halts execution and displays information about the error that occurs. This information is known as a stack trace. The stack trace in the above example tells us more about the error, such as the thread —
"main" — where the exception occurred, the type of exception —
java.lang.ArithmeticException, a comprehensible display message —
/ by zero, and the exact methods and the line numbers where the exception may have occurred.
The preceding exception could have been created explicitly by the developer as it is the case in the following code:
Note that when
b equals zero, there is no return value. Instead of a
java.lang.ArithmeticException generated by the Java interpreter itself, it is an exception created by the coder. The result is the same. It shows you that an exception is an object. Its main particularity is that it can be thrown. A exception object must inherit from
java.lang.Exception. Standard exceptions have two constructors:
- The default constructor; and,
- A constructor taking a string argument so that you can place pertinent information in the exception.
|Code section 6.1: Instance of an exception object with the default constructor.
|Code section 6.2: Instance of an
This string can later be extracted using various methods, as you can see in the code listing 6.2.
You can throw any type of Throwable object using the keyword
throw. It interrupts the method. Anything after the throw statement would not be executed, unless the thrown exception is handled. The exception object is not returned from the method, it is thrown from the method. That means that the exception object is not the return value of the method and the calling method can be interrupted too and so on and so on...
Typically, you'll throw a different class of exception for each different type of error. The information about the error is represented both inside the exception object and implicitly in the name of the exception class, so someone in the bigger context can figure out what to do with your exception. Often, the only information is the type of exception, and nothing meaningful is stored within the exception object.
Oracle standard exception classes
The box 6.1 below talks about the various exception classes within the
Box 6.1: The Java exception classes
|Figure 6.2: The exception classes and their inheritance model in the JCL.
By default, when an exception is thrown, the current method is interrupted, the calling method is interrupted too and so on till the
main method. A thrown exception can also be caught using a
catch statement. Below is how a
catch statement works:
|Code section 6.3: Division into a
The executed code lines have been highlighted. When no exception is thrown, the method flow executes the
try statement and not the
|Code section 6.4: Catching 'division by zero' errors.
As there is a thrown exception at line 5, the line 6 is not executed, but the exception is caught by the
catch statement so the
catch block is executed. The following code is also executed. Note that the
catch statement takes an exception as parameter. There is a third case: when the exception is not from the same class as the parameter:
|Code section 6.5: Uncaught exception.
It is as if there is no
catch statement. The exception is thrown to the calling method.
catch statement can contain several
catch blocks, to handle different exceptions in different ways. Each
catch block must take a parameter of a different throwable class. A thrown object may match several
catch block but only the first
catch block that matches the object will be executed. A catch-block will catch a thrown exception if and only if:
- the thrown exception object is the same as the exception object specified by the catch-block.
- the thrown exception object is the subtype of the exception object specified by the catch-block.
This means that the
catch block order is important. As a consequence, you can't put a
catch block that catches all the exception (which take a
java.lang.Exception as parameter) before a
catch block that catches a more specific exception as the second block could never be executed.
At line 14, we use a multi-catch clause. It is available since the JDK 7. This is a combination of several catch clauses and let's you handle exceptions in a single handler while also maintaining their types. So, instead of being boxed into a parent Exception super-class, they retain their individual types.
You can also use the
java.lang.Throwable class here, since Throwable is the parent class for the application-specific Exception classes. However, this is discouraged in Java programming circles. This is because Throwable happens to also be the parent class for the non-application specific Error classes which are not meant to be handled explicitly as they are catered for by the JVM itself.
finally block can be added after the
catch blocks. A
finally block is always executed, even no exception is thrown, an exception is thrown and caught or an exception is thrown and not caught. It's a place to put a code that should always be executed after an unsafe operation like a file close or a database disconnection. You can define a
try block without
catch block, however, in this case, it must be followed by a
Example of handling exceptions
Let's examine the following code:
|Code section 6.7: Handling exceptions.
In the code section 6.7,
methodC is invalid. Because
methodB pass (or throw) exceptions,
methodC must be prepared to handle them. This can be handled in two ways: a
catch block, which will handle the exception within the method and a
throws clause which would in turn throw the exception to the caller to handle. The above example will cause a compilation error, as Java is very strict about exception handling. So the programmer is forced to handle any possible error condition at some point.
To work correctly, the original code can be modified in multiple ways. For example, the following:
|Code section 6.8: Catching and throwing exceptions.
methodB will be handled locally, while
SomeException will be thrown to the caller to handle it. Most of the developers are embarrassed when they have to choose between the two options. This type of decision should not be taken at development time. If you are a development team, it should be discussed between all the developers in order to have a common exception handling policy.