20. Handling exceptions
Handling exceptions
Class ConsoleCylinder contains merely a main() method, and usually we don't allow main() to throw exceptions. However, for demonstration purposes we will ignore this, and adjust ConsoleCylinder to show how exceptions are caught and handled.
If the user enters a value into a dialog box produced by DialogCylinder that is not a number, a NumberFormatException will occur. This is a run-time exception so Java does not insist that we deal with it. In some large applications it can be impossible to check for all run-time exceptions; in this case however the application is so small as to make dealing with run-time exceptions simple. There are two ways in which we can deal with this:
- We can admit that our class might throw a
NumberFormatException, and do nothing about it so that the buck will be passed up the stack and left to the interpreter to handle:public class DialogCylinder { public static void main(String args) throws NumberFormatException { ... } }This will not really solve all the problems though, because we'll still be left with code execution hanging unreturned to the console. - We can write our own exception handler to deal with the problems.
The try ... catch construct
Handling an exception in Java involves separating the error-handling code from the 'regular' code. This is done by using a try{} catch(){} construct, which allows us to group the two types of code into separate blocks. In the try{} block, we write the 'regular' code; in the catch(){} block we write the code that deals with the exception. Here is the DialogCylinder class re-written for you to illustrate this:
Now, if an exception occurs while the regular code is executing within the try{} block, execution will jump to the catch(){} block where the exception is handled. In this case, we construct our own error message for the user, and make sure that execution doesn't hang without returning to the console by executing the System.exit(0); statement that would otherwise be missed. The image below illustrates DialogCylinder handling a NumberFormatException:
The catch(){} block
Let's take a closer look at the code. The try{} block is quite simple - the code that does the work of the main() method is placed inside this block. The catch(){} block is used to contain the code that deals with the exception that the code in the try{} block might throw. The catch() statement has parentheses which are used to hold the exception type or group that will be handled. For example in the DialogCylinder class, a NumberFormatException is handled and so an object of this class is defined in the catch(NumberFormatException e) statement. Essentially then, when an exception is thrown in Java, what is really thrown is an object - an object of the Exception class or one of its subclasses.
Inside the catch(){} block, we can use the NumberFormatException object to help us handle the exception. The exception object has a number of methods which it inherits from java.lang.Throwable; the most useful of these for our purposes is the getMessage() method. This method returns the detail message string of this particular object, and in the case of DialogCylinder we use it to add detail to the error message string that we return to the console when a NumberFormatException occurs:
} catch(NumberFormatException e){ System.out.println("\nIllegal input value - NumberFormatException: " + e.getMessage() + "\nApplication execution terminated."); System.exit(0); }This produces the error message in the image above.
'So', you may ask, 'how is this really different from what happens if we don't handle the exception?' Well, in a sense the result isn't very much different. However, the mechanism by which this result is achieved is different - in particular, it's firmly under our control. This means that we as programmers can decide what happens next when an exception occurs, and in this particular case it allows us to prevent the code hanging and not returning to the console, which is a very useful difference. Also, we don't have to produce the rather short and somewhat technical message that's in the code above. To make things more friendly for the user, we might decide to do something like this:
System.out.println(e + "\nYou have entered a value in a dialog box " +
"that isn't a number - this has caused the program to stop " +
"working because it needs to do a calculation with the " +
"entered value.\nPlease run the program again, " +
"and enter a number when asked to do so.");
Groups of exceptions
We don't have to catch a single exception with an exception handler. We can include more than one catch(){} statement after a try{} block - like this:
try {
...
[some code that uses an array]
...
} catch(ArrayIndexOutOfBoundsException e) {
...
} catch(NegativeArraySizeException e) {
...
}
Alternatively, we can catch all exceptions in a defined group by using an exception superclass:
} catch(RuntimeException e){
...
}
The catch(){} statement above will catch all runtime exceptions. Note however that our ability to tailor the response to an exception decreases the coarser our catching mechanism is. The code immediately above allows only a single response to many different exceptions, which forces the response to be generic. In fact, we could simply write:
} catch(Exception e){
...
}
but for obvious reasons this is undesirable, and is strongly discouraged. Using exception superclasses to handle exceptions only makes sense in certain rare cases (i.e. when there is no other sensible way to do it), and is generally regarded as laziness.
The finally{} block
Java error handling allows use of a finally{} code block. This block will execute no matter what happens in the body of the method. This allows us to clean up after our code has executed, and execute instructions that need to be executed whatever circumstances code execution terminates under. The finally{} block is appended after the last catch(){} block.
To see the usefulness of this, imagine that we were dealing with a class that uses javax.swing components, and which has a method with three catch(){} blocks. Each of these must execute System.exit(0); in order to terminate the application safely. This produces duplication of code - something we try to avoid if we can. Instead, by placing this line of code in the finally{} block, we can make sure that it always executes no matter what happens. In fact, it's no longer necessary to include this line of code in the try{} block either, because no matter what happens it will be executed.
try {
...
} catch() {
...
} catch() {
...
} catch() {
...
} finally {
System.exit(0);
}
We can now write the final version of DialogCylinder as follows: Notice that now the only place that System.exit(0); appears is in the finally{} block. Use the link below to download the final version of class DialogCylinder.java; compile and run it, and try entering both legal and illegal data into the dialog boxes.






