Exceptions
Peter
Komisar ©
Conestoga College minor revision 5.1 / 2006
Exception Hierarchy
Exception handling
is a plan for changing a program's flow of control
when an
unexpected event
or error occurs. Control is diverted to a part
of the program which
communicates with the user. Exceptions are
thrown by unacceptable
conditions, such
as dividing an integer by
zero or trying to access an array element
outside of it's defined bounds.
//
Exceptions are thrown by unacceptable conditions arising
Like
most everything in Java, exceptions are handled using an object-
based
model. Program problems are encapsulated in
class objects. When
an exception occurs,
an Exception object is created, specifically, one of
many specialized
descendants of Throwable, signaling to the
program it
has a problem to deal with.
//
Descendants of Throwable are created in response to error
conditions
The
programmer can choose to deal with an exception using a 'try...catch'
construct or it can pass the exception on
up, (the call stack) all the way to
main(
). If main doesn't handle the exception, the program will halt with
an
error report explaining what happened.
//
Different tactics may be used in dealing with exceptions shown
below
The
figure below shows the inheritance relationships that exist between
Throwable and it's descendants. The
tree describes two branches. There
is the Error branch and
the Exception branch. The Exception branch is
subclassed to form
the various checked exceptions. The
Exception class
is subclassed by the RuntimeException
exception class which is the
parent of the various runtime
exceptions.
The
hierarchy essentially describes
three groups of exceptions. Errors,
runtime exceptions and checked exceptions. A more detailed
diagram is
provided below the figure.
Notice even the detailed diagram doesn't show
all
the exception classes used in the JDK. There are also special
Exception
classes (i.e.SQLException)
defined inside packages devoted to particular
topics.
It makes sense to house these specialized Exception class
types
with the packages that use them.
The Exception Hierarchy
Object
|
Throwable
/
\
Exception Error
/
\
RuntimeException
The
various checked exceptions
/
( ClassNotFoundException,
InterruptedException etc. )
The various runtime
exceptions
( ArithmeticException,
ArrayStoreException, BufferOverflowException etc. )
Exception Hierarchy Tree
// Abbr. from JDK documentation. The Throwable hierarchy is in java.lang package
Object
|
Throwable
|
|__Error
|__Exception
|
|__ClassNotFoundException
|__CloneNotSupportedException
|__IllegalAccessException
|__InstantiationException
|__InterruptedException
|__NoSuchFieldException
|__NoSuchMethodException
|
|__________RuntimeException
|
|__ArithmeticException
|__ArrayStoreException
|__ClassCastException
|__IllegalArgumentException
|
|__IllegalThreadStateException
|
|__NumberFormatException
|
|__IllegalMonitorStateException
|__IllegalStateException
|__IndexOutOfBoundsException
|
|__ArrayIndexOutOfBoundsException
|
|__StringIndexOutOfBoundsException
|
|__NegativeArraySizeException
|__NullPointerException
|__SecurityException
|__UnsupportedOperationException
//
The Throwable hierarchy is all in java.lang package Throwable
is parent to
//
Exception and Error. Exception class includes checked and runtime
exceptions
Checked & Runtime Exceptions & Errors
Checked
Exceptions //
expected errors that may happen in a correct program
Checked exceptions arise
in a correct program from problems arising
during a program's
use. A user might make a typing error and enter a
number where a
letter should be located. A power failure might cause
an
interruption in a network socket connection causing an
IOException
to be thrown. These are types of error a programmer
can expect to occur
and needs to be ready to handle.
If a situation
arises that may cause a checked exception to be raised
but the
programmer hasn't provided some code to handle the exception
the
compiler will report the problem to the programmer. For instance
the
compiler might report something like, "java.lang.SomeKindOfException
must be caught or declared to be
thrown." The following example won't
compile. The
compiler reports InterruptedException must be caught.
Example
class
CheckEx{
public
static void main(String args[]){
Thread.sleep(1000);
}
}
// doesn't compile. reports InterruptedException
Runtime
Exceptions
Runtime exceptions are
thrown by what are commonly called program
bugs. A typical
example of this is when a loop is used that incorrectly
references an
array element that is beyond the range of elements that
have been
defined. For instance, an attempt is made to access the
tenth array
element in an array that only has nine elements. This will
cause
an ArrayOutOfBounds exception to be thrown.
// bugs that show up at runtime need to be fixed by the
programmer
In a correct program,
runtime exceptions should never occur so you
are not required to
handle them. It is assumed the programmer will fix
any problems that
cause these runtime exceptions to occur. In the
case of runtime
exceptions, they show up and reported when the
program is run.
Runtime exceptions are often harder to repair as they
can result
from what is correct code from the compilers point of view.
The
code is correct in the sense it logical leads to the result that
causes
the runtime exception to be thrown. To show the 'Array Out
of Bounds'
problem the following sample compiles fine but at
runtime throws an
ArrayOutOfBounds exception.
Example
class ArrayBug{
public static void main(String args[]){
float [ ] f={ 1.01f };
System.out.println( f[1] );
}
}
// compiles but fails to run. ArrayOutOfBounds exception is thrown
Errors
Errors describe
environmental problems which may be rare or difficult
to recover
from such as running out of computer memory. You are not
expected
to handle Error class objects. These
should be regarded as
environmental flaws. Hopefully you will
never see them.
User Defined Exceptions and the throw Statement
Before we look at
the error handling strategies Java supplies, we will
look at how a
programmer can take control of when an exception is
thrown using
the keyword throw. As well, we need to consider that
Java
is an object-oriented environment. Even exceptions are defined
as
classes that are used as templates to create exception objects.
This opens the possibility for the Java programmer to use inheritance
to define custom exception classes.
Custom exception classes
are
created by extending Exception class.
The programmer can use the
'throw' keyword in conjunction with the
given exception class
constructor to generate objects of this class in
response to
certain circumstances. Exceptions are thrown implicitly
when
something illegal has occurred in a program. In contrast,
exceptions are thrown explicitly with the keyword 'throw'.
//
the 'throw' keyword together with object-oriented inheritance
allows
// custom exceptions to be created
In the following
example, a custom exception class called PartyException
is created
by extending Exception. This exception object is thrown, in
the
BirthdayCalendar call if some birthday condition is met. ( This is
not good coding in
the sense that exception handling is there to handle
when things go wrong and not to
do a sort of flow control as is exampled
below. )
Example
class
PartyException
extends Exception{
PartyException( ){
System.out.println("Time for a party");
}
}
//
PartyException extends Exception & prints to console its
"Time for a party"
class
BirthdayCalendar{
public static void main(String[] args)
throws PartyException {
// to get pass the compiler we show main throwing the exception
// Assume we have a list we loop through, itemizing
friend and and family birthdays
// Also assume the current date (plus some advance time)
is compared to the list
// of birthdates. If one is found, a
boolean value, a_birthday, is set to true
if (a_birthday) throw new PartyException( );
}
}
The 'throw'
keyword is used in conjunction with the PartyException
class
constructor and the 'new' operator. Also, the suffix, 'throw
PartyException' has been appended to the main methods signature.
This was necessary in order for the code to
compile. This satisfies
the rule that states a method that may cause
an exception to be thrown
must be defined showing explicitly
which exception it can throw.
// checked
exceptions must be caught - runtime exceptions may be caught
(Notice
we didn't have to add a throws clause to main when we were
dealing
with the ArithmeticException. This
is because ArithmeticException
is a runtime exception. As was
discussed earlier, the programmer is not
mandated to catch runtime exceptions. Not to
say it would not have
been a good idea!
By
the way, nothing stops us from catching runtime
exceptions.
We will example this later in the note.
Error Handling Strategies
There are three
strategies one can describe when dealing with exceptions.
The first is
more a lack of strategy, 'Don't do anything at all!'. It is
possible
to take this approach and you get the expected results.
The second is to
defer dealing with the exception to a higher
scope. The second approach
may be described as passing the problem
on. The final strategy is to use
'try catch and finally' blocks to
handle the occurrence of exceptions.
1. Do Nothing
The first case scenario
in dealing with exceptions is to do absolutely
nothing! The Java
compiler only allows you to get away with this with
RunTime and
Error exceptions. This is not a wise approach but is
included
more as a demonstration of the simplest scenario. The
following
example shows an exception waiting to happen.
Example
class Trouble{
public static void main(String[] args) {
int x =1 / 0;
System.out.println("We will never see this line printed");
}
}
// this will
compile but an exception will be thrown by the runtime environment
Observe the class
provides no plan to deal with the inevitable exception
it is set to
create. In this case the exception is thrown in the main( )
method. Since main is the program's point of entry, it doesn't
have far
to go before being registered as an exception with the
user. The
Exception object is created and passed to the runtime
which prints an
error report to console stating an
ArithmeticException has been thrown
and exits the program. If you
look at detailed class exception hierarchy
described earlier you will
find the RuntimeException subclass,
ArithmeticException,
listed.
Dividing by zero with floating point numbers /* Floating
point numbers will not throw the runtime ArithmeticException. Instead class X1{ |
2. Pass the Exception On with a 'throws' Statement
The keyword throw
causes an exception to be thrown. The keyword
throws is
used differently. This keyword is appended to a method's
signature
to show the method is capable of throwing an exception.
The
throws clause declares that the use of the given method may
result in an exception (of the type declared) being thrown. This
exception, if it is not handled by appropriate code locally, will
be
passed 'up' into the next highest scope containing this method
call.
The following example
shows how a throws clause is appended to
a method signature.
public static void sleep(long millis) throws InterruptedException
//
The sleep( ) method is a static Thread class method. It has a
// couple
overloaded forms.
The commonest form takes an int
// value representing
milliseconds.
Any
method that throws an exception (and does handle the exception
itself
) must be itself declare it throws
that exception. In the following
example, the rest method is
defined calling sleep within it. ( Notice it
calls the static method
sleep, off of the Thread class name.
This is
good form as it signals the reader that sleep is a static
method.)
public void rest( ) throws InterruptedException{
Thread.sleep(60000);
// sleep might be interrupted and throw InteruptedException
}
Because
sleep can throw an InterruptedException, the compiler now
requires
the enclosing method, rest( ), to
declare it throws this
exception. If rest( ) is now called in
another method that method too
must be declared as throwing
InterruptedException and so on all the
way up to main( ). There
is an alternative. The new methods can be
written to handle any
exceptions that occur.
This approach is useful
in defining one or a set of methods that may
cause an exception to
be thrown. In the final analysis you will need
to use Java's
technique for handling exceptions using try catch and
finally
blocks.
3. Handle the Exception with try catch & finally blocks
The recourse Java
provides for disposing or handling exceptions that
may occur in a
program are structures built using 'try catch and
finally'
blocks. Any method that may throw an exception is
'called' from
inside the try block. A catch block is provided to
handle the exception
should that exception be thrown. The optionally
'finally' block in called
in any event barring cutting power to the
machine. The 'try catch finally'
blocks take the following form.
try { // block
} opt.[ catch ( Exception
e ) ] opt.[
finally { // block } ]
Rules Governing the Use of try catch and finally Blocks
There are a number of
rules that describe the use of these exception
handling structures.
First, for any try block there has to be at least
one catch or
finally statement. (Even an empty try block insists on
having
either a catch or finally statement.)
Example1
try{
System.out.println("No exception possibility");
}
finally{
System.out.println("Still need a catch or a finally
statemenet");
}
In the usual case, the
try block will contain a method that may lead to an
exception
being thrown. In this case a catch statement must be provided.
The
compiler will always tell you if this is the case.
Example
2 try{
Thread.sleep( 1000 );
}
catch(InterruptedException ie){
System.out.println("Need a catch statment if an exception may be
thrown");
}
The third rule is
that there can at most only one finally statement. There
can however
be any number of catch statements.
A fourth rule may be
described. Exception subclasses must be caught
before their more
general parent classes. Both notions described in rule
three and
four are captured in the following example.
Example
3 try{
Thread.sleep( 1000 );
}
catch(InterruptedException ie){
System.out.println("Need a catch statement if an exception may
be thrown");
}
catch(Exception ie){
System.out.println("The more general exception must follow the
subclass");
}
// the compiler would not let this catch clause be placed before the
one above it
finally{
System.out.println("The compiler will only allow a single
finally block");
}
The following example
shows how the rest( ) method would be rewritten in order
to
handle the exception thrown by sleep( ).
Example
public void rest( ){
try{
Thread.sleep(60000);
}
catch(InterruptedException ie){
System.out.println("Interrupted Exception");
}
In
the following code section of an imaginary method, a variety of
things can happen which effects the
control path taken.
Code sample
// ...
// somewhere inside a method
//...
try{
// exceptions waiting to happen
}
catch( SubException es){
// say or do something
}
catch(
AnotherException ea){
// say or do something
}
catch(
ParentException ep){
// say or do something
}
finally{
// code executed in any case
}
// ...
// the next line following the finally block of the method
// ...
No
Exception Occurs
If
no exception occurs, the code in the try block finishes
execution,
the finally block executes and
execution resumes
at the line following the finally block.
A
SubException Occurs
If
a SubException is thrown, execution leaves the try block
at the point
the exception was raised and goes to
the catch
block associated with SubException. After this code
is
executed, the finally block is executed
and execution
proceeds at the line following the
finally block.
ParentException
is Thrown
If
a ParentException is thrown, execution exits the try
block and
proceeds at the catch block
associated with
the ParentException. Once this block executes, the
finally block executes and control
proceeds at the line
following the finally block.
An
Unknown Exception Occurs
If
an unknown exception occurs, execution proceeds
directly to the
finally block. When the finally
block is
finished executing, control leaves the method entirely.
This is an uncaught exception which
will next appear
in the caller.
The Exception Class
The
Exception class has a couple constructor forms and several
useful
methods which can be used to augment
the information
that is returned when an exception occurs.
For an example look
at the end of the
assignment section.
Constructors
public Exception( ) |
Constructs an Exception with no specified detail message. |
public Exception(String s) |
Constructs an Exception with the specified detail message. |
Methods inherited from Throwable 3 of 7 methods
String
|
Returns the error message string of this throwable object. |
void
|
Prints this
Throwable and its backtrace to the standard error stream.
|
String
|
Returns a short description of this throwable object. |
Using
printStackTrace( )
The printStackTrace(
) method is useful to unravel a set of events
that lead back to the
location where the error occurred. Following is
some Java
code that makes several nested calls that inevitably lead
to an
exception being thrown. The printStackTrace( ) method is called
to trace through the call stack back to where the error occurred.
The Call Stack
Method
calls are, at the machine level, directives to go to an address
and
run the list of instructions that
are stored there. If from this
location a call is made to a nested
method, information about the first
method has to be stored somewhere while the computer is
busy
attending to the new block of information.
The computer uses a data
structure called
a stack to store the details of where in a method's
instruction list
execution is at, before going off to
execute the new
block of code. ( The following is a figurative
description of what goes
on inside the runtime
environment at the machine level. )
Diagram
of nested methods being called in a series
main(
) |
| --getA( ) |
|
| --getB( ) |
|
|---getC( ) |
|
| // exception
thrown
The
stack trace shows the path in reverse order of how it was traversed
to get to the point where the
exception was thrown .
The
following code sets up a nested set of methods that will end up
throwing an exception. The
printStackTrace method is used to show
the behavior that occurs
when an exception is thrown.
Example
public
class Trace{
public static void
main(String[]args){
getA(
);
//
line 3 of main where a method calls the method that calls the bad
method
}
static void getA( ){
getB(
);
// line 6 call on method that calls the bad method
}
static void getB( ){
getC(
);
// line 9 first call on bad method
}
static void getC( ){
try{
int i=1;
int j=0;
int k= i /j;
//
line 15 where the trouble starts
}
catch
(ArithmeticException ae){
ae.printStackTrace(
);
// here is the printStackTrace( ) method being called
}
}
}
This code results in the
following output to the command line. Notice
the stack trace reports
from where the error occurred proceeding to
report as it works back
up the calling stack finally to main.
Output of the printStackTrace( ) method
C:\NewCode>java
Trace
java.lang.ArithmeticException: / by zero
at Trace.getC(Trace.java:15)
at Trace.getB(Trace.java:9)
at Trace.getA(Trace.java:6)
at Trace.main(Trace.java:3)
Using the getMessage( ) method
The
getMessage( ) method is used to return the String message that
is
passed into
the Exception constructor that is
designed to receive
an error message.
Example
public Exception(String s) //
getMessage( ) returns String
For
example a class can be built extending the Exception class,
building
a constructor that accepts a String
object and then
invoking the appropriate parent constructor using
a super( )
constructor call.
Example
1 class
PowerDown extends
Exception{
PowerDown ( String reason ){
super(reason);
}
PowerDown( ){ } // no
args constructor
}
The
new Exception class can then be used in different circumstances
and
the getMessage( ) method can return
a variety of appropriately
related messages.
Example
2 class
Reactor {
static int temp;
public static void main(String[]args){
temp =(int)( Math.random( )*2400 ) ;
try {
if(temp>2000)
throw new PowerDown("temperature too high");
else if( temp< 200)
throw new PowerDown("temperature too low");
}
catch(PowerDown pd){
System.out.println(pd.getMessage( ));
}
}
}
Assertions
// for reference, not on
final
There
are implicit assumptions in most programs we write. Though
humans
think they are obvious, computers
don't. An example, we
think of our ages as being only
positive numbers while computers
have no
problem with a person being -3. We assume
that interest
rate at the bank is a positive value while charges are
subtracted
from our accounts. We
would not be comfortable with a negative
interest rate although
we might not mind negative charges being
subtracted from our account. What is logically
not feasible is possible
for the computer. A technique to test for
different assumptions we
make is
called assertions.
An
assertion is an assumption you believe to be true. Assertions
are
used to help assure quality control
of software, so that it is as
bug free as possible. The following
examples are from Cay
Hoorstman's
'Computing Concepts with Java 2 Essentials'. It
has been supplemented with some real values and a main
method where it's methods are called.
Example Adapted from Cay Hoorstman's 'Computing
Concepts
with Java 2 Essentials' // supplemented with values and main( )
If
an Exception is thrown by the assertion it will show up in the
stack
trace.
The 'asserts' Keyword
To
simplify and perhaps ensure uniform implementation Java has
added the
'asserts' keyword to the JDK1.4 to
do assertions. This
will have immediate impact on old code
compiled in JDK1.4 only
on code that was unlucky enough to have used
the 'asserts' word
as an identifier.
The compiler is supplying a transitional accommodation
where
it will temporally allow asserts to co-exist as an identifier or
as a
keyword. Practically we can consider
'asserts' to be a keyword
from this point forward.
The
'asserts' keyword takes care of creating testing code and may
be left
in code after assertion testing as a
protection against further
modifications that may result in
bugs.
The following code
is included just to emphasize that runtime
exceptions can be caught like other exceptions.
Example of a
Runtime Exception Being Caught Using a try catch Block
class
TroubleFix{
public static void main(String[] args) {
try{
int x =1 / 0;
System.out.println(x + " But x will never be
printed!");
}
catch(RuntimeException re){
System.out.println("\n " + "Error Description: "
+ "\"" + re.getMessage( ) + "\"");
}
System.out.println("Proceeding Like Nothing Happened!" +
"\n ");
}
}
Self Test Self Test With Answers
1)
Select the incorrect statement.
a)
Checked exceptions arise in a correct program.
b) In a
correct program, runtime exceptions should never occur.
c)
You are not expected to handle Error class
objects.
d) Throwable are a subclass of the Checked Exception
category
2) The following
code will
class Dv{
public static void main(String[] args) {
System.out.println("Running Test");
int x =1 / 0;
}
}
a) not compile
b)
compile but fail to run
c) compile and run and throw a runtime
exception
d) compile and run successfully without error reports
3) The sleep
method throws InterruptedException.
What will the following code do?
class
X2{
public static
void main(String args[]) throws InterruptedException {
Thread.sleep(100);
}
}
a) does not compile
b)
compiles but fails to run
c) compiles and runs and throws a
runtime exception
d) compiles and runs successfully but may throw
an exception
4) Which of the following statements regarding exceptions is not correct?
a) there can only be one
finally block
b) for every try block there must be either a catch
or finally block
c) for every try and catch block there must also
be a finally block
d) there can be any number of catch blocks
Exercise
Use the examples in
the note to answer the following questions.
Q1.Write
a class with no error protection that throws a divide by zero
exception.
// shows doing nothing
Q2.Write
an exception class called Flat and throw it from an if clause
with a
boolean value, NoAir. (Don't use try and
catch in this question.
This will require
declaring main( ) as throwing the
exception.)
//
shows throws being used
Q3
In a try clause throw Exception. In the corresponding catch clause
catch the Exception object.On catching it, print to screen "Exception
caught",
and from a finally block, print to screen
the word continuing.
//
demonstates try catch finally
Q5
Create an extension of Exception that uses the constructor of the
Exception class that allows a string
message to be
provided. You
will need to use super( ) to invoke the
parent constructor to get this
behaviour
in your extension of the Exception class. Then
use the
getMessage( )
method to extract this string when the
exception is
thrown.
( If you have trouble with this question, don't fight it too long. A
sample of code that illustrates
this formulation
is included at the
end of the note. )
// shows getMessage( ) in use
Example
class
MotorFailure extends Exception{
MotorFailure(String cause){
super(cause);
}
}
// should really add the no-args constructor as well
// now in case
someone extends this class
//
Now this exception class can be instantiated with the constructor
// that takes a descriptive string. This string is returned if getMessage( )
// is called on the
exception object thrown.
class
MotorTest{
public static void
main(String[]args){
try {
throw new
MotorFailure("blown gasket");
}
catch(MotorFailure mf){
System.out.println( mf.getMessage( ));
}
}
}