Handling ExceptionsJ8 Home « Handling Exceptions
Now we know what the Java exception hierarchy looks like and the classes involved its time to start handling exceptions which may occur within our code. Exception control in Java is achieved using the try,
catch, finally, throw and throws keywords.
The throw keyword allows us to throw exceptions and we can declare exceptions from within our methods using the throws keyword. Both these keywords are discussed in much greater detail in the
next lesson Declaring Exceptions.
In this lesson we focus on the try, catch and finally keywords and how we use them to handle exceptions in our code. The try keyword is used with a code block
to put the code that may cause an exception in. The catch keyword is used with a code block to put the code to handle an exception in. The finally keyword is used with a
code block to put the code that must be run whether there is an exception or not.
try catch finally Construct Top
The following table shows the different forms of the try catch finally construct.
| Construct | Description |
|---|---|
try catch | |
try { |
Execute statements in try code block.Execute statements in catch code block. |
try catch finally | |
try { |
Execute statements in try code block.Execute statements in catch code blockExecute statements in finally code block. |
try finally | |
try { |
Execute statements in try code block.Execute statements in finally code block. |
try with multiple catch | |
try { |
Execute statements in try code block.Execute statements in catch code block 1.
Execute statements in catch code block 2.Execute statements in catch code block N. |
try with multiple catch and a finally | |
try { |
Execute statements in try code block.Execute statements in catch code block 1.
Execute statements in catch code block 2.Execute statements in catch code block N.Execute statements in finally code block. |
try catch finally Rules Top
When using the try catch finally construct there are certain rules that must be adhered to or you get a compiler error:
- When using a
tryblock it must be accompanied by acatchblock, afinallyblock or both. - When using a
catchblock it must immediately follow thetryblock. - When using multiple
catchblocks they must go in order from the most specific error to the most generic as discussed later in the lesson in Exceptions And Polymorphism. - When using a
finallyblock it must immediately follow the lastcatchblock, or thetryblock when nocatchblock is present.
Creating A Folder For Our Flow Control Source Files
As we are about to do the first code example of a new section lets create a folder for our Flow Control files, in Windows this would be:
double click My Computer icon
double click C:\ drive (or whatever your local drive is)
right click the mouse in the window
from the drop down menu Select New
click the Folder option and call the folder _FlowControl and press enter.
Using try catch Top
The try catch construct is the most commonly used of the three forms of the try catch finally construct listed in the table above. To get us started we will write a class that will cause a
runtime error by trying to divide by zero.
package info.java8;
/*
A DivideByZero class
*/
public class DivideByZero {
public static void main(String[] args) {
int a = 5 / 0; // JVM will not like this
}
}
Save, compile and run the DivideByZero class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running our DivideByZero class. We got an ArithmeticException exception when we tried to divide by zero as expected.
This is not a very elegant termination of our program and will mean nothing to a general user of the software. It also means termination of the program so any code won't get executed. We will rework the code using a
try catch to output a message instead.
package info.java8;
/*
A DivideByZero class
*/
public class DivideByZero {
public static void main(String[] args) {
/*
We put the code we think might cause a problem in a try block
*/
try {
int a = 5 / 0; // JVM will not like this
}
/*
We handle the exception in a catch block
*/
catch (Exception ex) {
System.out.println("We caught exception: " + ex);
}
System.out.println("We can just continue on now.");
}
}
Save, compile and run the DivideByZero class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running our reworked DivideByZero class. This time we catch the ArithmeticException exception and continue on with our code.
Using try catch finally Top
We use the try catch finally construct when we want to action some code regardless of the outcome of an action that might cause an exception. Whatever we put into the finally code block
is guaranteed to run whether an exception occurs or not. There are many reasons to include a finally statement, such as freeing resources and removing code duplication. In the following examples we will
use a try catch and then remove code duplication by replacing it with a try catch finally version:
package info.java8;
/*
A NumberFormatException class
*/
public class NFE {
public static void main(String[] args) {
System.out.println("We are turning on the timer.");
try {
System.out.println("Timer set for " + Integer.parseInt(args[0]) + " minute(s).");
System.out.println("We are turning off the timer.");
}
catch (Exception ex) {
System.out.println("We caught exception: " + ex);
System.out.println("We are turning off the timer."); // Code duplication
}
}
}
Save, compile and run the NFE class in directory c:\_FlowControl passing 1, then run again passing one.
The above screenshot shows the output of running our NFE class when we pass 1 andone. We got a NumberFormatException when we passed one. The point
is that in this rather contrived class we always want to send a message that the timer is turned off and the only way to do that is to duplicate code. We will rework the class to use finally to get rid of
the duplicated code:
package info.java8;
/*
A NumberFormatException class
*/
public class NFE {
public static void main(String[] args) {
System.out.println("We are turning on the timer.");
try {
System.out.println("Timer set for " + Integer.parseInt(args[0]) + " minute(s).");
}
catch (Exception ex) {
System.out.println("We caught exception: " + ex);
}
finally {
System.out.println("Finally will always get executed whether an exception occurs or not.");
System.out.println("We are turning off the timer.");
}
}
}
Save, compile and rerun the reworked NFE class in directory c:\_FlowControl passing 1, then run again passing one.
The above screenshot shows the output of running our reworked NFE class when we pass 1 andone. We got a NumberFormatException when we passed one. As you
can see the finally block always runs whether an exception occurs or not.
Using try finally Top
We can also use the try finally construct and maybe a possible reason for using this would be for an application that does some processing and closes down after freeing the resouces. In this example we will
use a try finally to print some simple messages:
package info.java8;
/*
A DivideByZero2 class
*/
public class DivideByZero2 {
public static void main(String[] args) {
System.out.println("Starting application.");
try {
int a = 5 / 0; // JVM will not like this
}
finally {
System.out.println("Finally is always processed.");
System.out.println("Closing application.");
}
}
}
Save, compile and run the DivideByZero2 class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running our DivideByZero2 class. We got a We got an ArithmeticException exception when we tried to
divide by zero as expected. The finally block always runs and in our example we print some messages before the runtime exception crashes the application.
Exceptions And Polymorphism Top
In all the above code examples we have been using the Exception object as the parameter within our catch blocks. The Exception class is the superclass of all exceptions, as
we saw when we looked at the Exception Hierarchy Diagram in the last lesson. We have been using the Exception class polymorphically to act like a catch all
for any exception objects that are created and thrown from its subclasses. We can have finer grained control over what we do about an exception by using multiple catch blocks catering for the
exceptions we need to handle. Lets look at part of the exception hierarchy from the last lesson and write some code to illustrate how we do this:
In the code below we have added some extra catch blocks to the NFE class we wrote earlier in the lesson:
package info.java8;
/*
A NumberFormatException class
*/
public class NFE {
public static void main(String[] args) {
System.out.println("We are turning on the timer.");
try {
System.out.println("Timer set for " + Integer.parseInt(args[0]) + " minute(s).");
}
catch (Exception ex) {
System.out.println("We caught exception: " + ex);
}
catch (NumberFormatException ex) {
System.out.println("An invalid number was passed: " + ex);
}
catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("An invalid argument was passed " + ex);
}
finally {
System.out.println("Finally will always get executed whether an exception occurs or not.");
System.out.println("We are turning off the timer.");
}
}
}
Save and compile and the reworked NFE class in directory c:\_FlowControl.
The above screenshot shows the output of compiling our reworked NFE class, we got a compiler error. The reason for the error is that we have the catch all Exception class in our
first catch block, which will catch every exception. This means the other two exceptions can never be reached. When we code multiple catch blocks, we have to work up the inheritance tree from
the most specific exception to the most generic. So in our code and using the exception hierarchy diagram above we can see that the catch blocks should be ordered NumberFormatException or
ArrayIndexOutOfBoundsException which are the lowest in the inheritance tree. The order of these two doesn't matter as they are at the same level of the inheritance hierarchy. The catch all
Exception comes after these two. In the code below we have put the catch blocks in the correct order.
package info.java8;
/*
A NumberFormatException class
*/
public class NFE {
public static void main(String[] args) {
System.out.println("We are turning on the timer.");
try {
System.out.println("Timer set for " + Integer.parseInt(args[0]) + " minute(s).");
}
catch (NumberFormatException ex) {
System.out.println("An invalid number was passed: " + ex);
}
catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("An invalid argument was passed " + ex);
}
catch (Exception ex) {
System.out.println("We caught exception: " + ex);
}
finally {
System.out.println("Finally will always get executed whether an exception occurs or not.");
System.out.println("We are turning off the timer.");
}
}
}
Save, compile and rerun the reworked NFE class in directory c:\_FlowControl with no arguments, then run again passing 1, then run again passing one.
The above screenshot shows the output of running our reworked NFE class with no passed arguments, when we pass 1 and one. We got an ArrayIndexOutOfBoundsException when
we didn't pass any arguments and output an error in the catch block for that exception. When we passed a 1 the code runs fine. When we pass one we got a NumberFormatException
and output an error in the catch block for that exception. As you can see the finally block always runs whether an exception occurs or not.
Lesson 2 Complete
In this lesson we looked at handling exceptions which may occur within our code.
What's Next?
In the next lesson we learn how to use the throw keyword which allows to throw exceptions from within our methods. We also investigate how to declare exceptions using the throws keyword.