Exceptions
Peter
Komisar ©
Conestoga College latest revision 5.5 / 2008
Exception Hierarchy
"Houston, we have a problem."
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 that has to
be handled.
//
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 Throwable Hierarchy
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, such
as SQLException,
that are defined inside packages
devoted to particular topics.
It makes sense to house
these specialized Exception class types
with the
packages that use them.
//
other 'dedicated' Exceptions reside in different packages
The
Exception Hierarchy
Object
|
Throwable / \
Exception Error / \
Checked Runtime
Exceptions Exceptions
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 letter
where a
number 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 must be caught
Runtime
Exceptions
Runtime exceptions are
thrown by what are commonly
called program bugs. A typical
example of this is when
a loop incorrectly references an
array element that is
beyond the range of defined elements. For example,
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 by Java 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 may be harder to spot
// as the compiler sees the code as being legal
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 need to
deal with them.
User Defined Exceptions & the throw Statement
The 'throw' Keyword
Before we look at
the error handling strategies that Java
supplies, we will look at how a
programmer can take
control of when an exception is thrown using
the keyword
'throw'.
As we know by now, Java
is an object-oriented environment.
Even exceptions are defined as
classes that are used as
templates to create exception objects.
This leads to 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' under conditions that the programmer chooses.
//
the 'throw' keyword together with object-oriented
// inheritance allows custom
exceptions to be created
In the following
example, a custom exeption class called
PartyException is created
by extending Exception. This
exception object is thrown, in the
BirthdayCalendar call
if some birthday condition is met.
( Note it is not
recommended coding practice to use
exception handling as a sort of flow control to do regular
coding. Exception handling is designed to handle to
unexpected or undesirable conditions. We might make
an 'exception' at this point as the following example is
easy to understand and illustrates clearly how 'throw'
works. )
Example
class
PartyException
extends Exception{
PartyException( ){
System.out.println("Time for a party");
}
}
//
PartyException extends Exception. When it's constructor
// is invoked,
"Time for a party" is printed to console
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 family
// birthdays
Also assume the current date
(plus some advance
// time)
is compared to a list of birthdates. If one is found, the
// boolean
'a_birthday' is set to true
if (a_birthday) throw new PartyException( );
}
}
The 'throw'
keyword is used in conjunction with the 'new'
operator and the constructor of the PartyException class.
The suffix, 'throw PartyException' has been appended to
the main( ) method signature. This is necessary in order
for the code to
compile in it's current form. 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.
// while checked
exceptions must be caught,
// runtime exceptions may be caught
Notice
we didn't have to add a throws clause to main( )
in the earlier example, when we were dealing
with the
ArithmeticException. This
is because ArithmeticException
is a runtime exception.
As
was
discussed earlier, the Java programmer is not
mandated to catch runtime exceptions. Not to
say it
would not have been a good idea! Nothing stops us from
catching runtime
exceptions.
The following code
is included to demonstrate that runtime
exceptions may be caught like other exceptions. We are
ahead of ourselves again, as the try catch block is discussed
later in this 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,
to do nothing, is not really a stategy.
It is more a matter of stating the simplest scenario where an
Exception will inevitably be thrown. The Java
compiler only
allows you to get away with this with RunTime and
Error
exceptions. 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 at runtime
Observe the class
provides no plan to deal with the 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 the class exception hierarchy shown earlier you
will find ArithmeticException is a
RuntimeException sub-class.
Dividing by zero
with floating point numbers /* Floating point numbers will not throw the runtime ArithmeticException. Instead as mandated by the IEEE floating point standard a floating point number reports INFINITY when a divide by zero using floating point numbers is performed. */ class X1{ public static void main(String[] args) { System.out.println("Div Test"); double s=60; double t=0; double d = s / t; System.out.println(d); // prints 'Infinity' } } |
2.
Pass the Exception On with a 'throws' Statement
The keyword 'throw'
causes an exception to be thrown.
On the other hand, 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.
Declaration of the sleep( ) method Showing 'throws'
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.)
Example of a Method
Needing to Declare it Throws an Exception
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' & 'finally'
blocks.
3. Handle the Exception with try catch & finally blocks
The mechanism Java
provides for disposing of or handling
exceptions that might arise in a
program are flow control
structures called '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 optional
'finally' block
is called in any event barring the underlying operating system
failing or cutting
power to the
machine. The 'try
catch finally'
blocks take the following form.
Form of the 'try',
'catch' 'finally' Block
try { // block
} opt.[ catch ( Exception
e ) ] opt.[ finally { // block
} ]
The following example
shows how the rest( ) method
might be rewritten in order to
handle the exception
thrown by sleep( ).
Method That Throws an
Exception Rewritten
to Catch the Exception
public
void rest( ){
try{
Thread.sleep(60000);
}
catch(InterruptedException ie){
System.out.println("Interrupted Exception");
}
Rules
Governing the Use of 'try catch & 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.)
Example Showing a 'try' with a 'finally' Block
try{
System.out.println("No exception possibility");
}
finally{
System.out.println("Still need a catch or a finally
statemenet");
}
The 'catch' to the above
combination is that it only compiles
if the try block is incapable of throwing an exception which
sort of defeats the purpose of using the try block in the first
case. In the usual case, the
try block will contain a method
that may lead to an exception
being thrown. In this case a
'catch' block must be provided. The
compiler will always tell
you if this is the case.
//
could still be useful to make sure something is always
// done in the event that an unknown
exception is thrown
Example
Showing a Typical 'try' 'catch' Combination.
try{
Thread.sleep( 1000
);
}
catch(InterruptedException ie){
System.out.println("Need a catch statment if an exception may be
thrown");
}
The second rule is
that there can at most only
one finally
statement. There can
however
be any number of catch
statements.
A third rule may be
described. Exception
subclasses
must be caught before their
more
general parent classes.
Both notions described in rule last two rules are captured
in the following example.
Specialized
Exceptions are handled before more General
Exceptions
// also
examples a complete try catch finally Construct
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 line following the finally block
// ...
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 next enclosing scope.
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.
Exception
Constructors
public |
Constructs an
Exception with |
public |
Constructs an
Exception with |
Methods inherited from Throwable // 3 of 7 methods
String
|
Returns the error
message string |
void |
Prints this
Throwable and its backtrace |
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 Stack
" A stack is an abstract data type and data structure based on the principle of Last In First Out (LIFO). . . .Among other uses, stacks are used to run a Java Virtual Machine, and the Java language itself has a class called "Stack", which can be used by the programmer. " - wikipedia PS. It's like the PEZ candy dispenser -> PUSH in the candy --> POP it out Last in is first out! |
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 a
new block of code state information is stored so that
everything can pick up at the same place when the
branch code returns. (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 calling the method that calls the
// method that calls the 'bad'
method
}
static void getA( ){
getB(
);
// line calling method that calls the bad method
}
static void getB( ){
getC(
);
// line with first call on bad method
}
static void getC( ){
try{
int i=1;
int j=0;
int k= i /j; //
line 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
class
PowerDown extends
Exception{
PowerDown ( String reason ){
super(reason);
}
//
no
args constructor
PowerDown( ){ }
}
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.
Another
example, 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
seems illogical
to us is often feasible for the
computer. A technique to test for
different assumptions
we make is
referred to as making '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 // only for reference
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 created before JDK 1.4.x.
If this code was unlucky
enough to have used
the 'asserts' word as an identifier
an compiler error will result.
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.
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 Good Luck on the Exam!
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( ));
}
}
}