Handling ExceptionsS2C 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
ConstructTop
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
RulesTop
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
try
block it must be accompanied by acatch
block, afinally
block or both. - When using a
catch
block it must immediately follow thetry
block. - When using multiple
catch
blocks 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
finally
block it must immediately follow the lastcatch
block, or thetry
block when nocatch
block 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.
/*
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.
/*
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:
/*
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:
/*
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:
/*
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 PolymorphismTop
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:
/*
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.
/*
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.