
CS102: Exception Handling
Copyright © 1999,
Kenneth J. Goldman
Intro
- Goal:
To allow computation to abort in the middle and communicate that fact (and why)
to the caller, so the caller can respond to the failure appropriately.
- Overview:
- Caller attempts a computation by making a method call
- One of two things happen, Either:
- the call completes succesfully and the return comes back, OR
- the call aborts in the middle and throws and exception
- If an exception was thrown, the caller catches it and reacts apprpriately
- An Exception is an object that describes an error condition.
- To "throw" an exception is to notify the caller that the error occured.
- To "catch" the exception is to receive that notification (and handle the problem as
appropriate)
- If an exception is not caught, it propagates outward through nested code and then up
the call stack. If it's not caught along the way, it goes all the way to the top of the
call stack and the JVM prints a message and exits.
Example 1: Handling exceptions
- Suppose you are writing a method that takes in an int value (supposedly) an index
into an array in your object. You are supposed to return the Object reference at
that location in the array.
You could check the index against the length of the array first, as in
-
Object elementAt(int i) {
-
if (i < myArray.length) {
- return my Array[i];
}
else {
- return null;
}
}
OR... you could assume everything will be ok and handle the exceptional case only when it
arises...
-
Object elementAt(index i) {
-
try {
- return myArray[i];
}
catch (ArrayIndexOutOfBoundsException e) {
- return null;
}
}
Of course, you could still get a NullPointerException if myArray is not initialized. We could
catch the NullPointerException, but since myArray is an instance variable (presumably private),
we should be able to maintain an invariant that myArray is never null (initialize it in the
constructor and never set it to null in any method).
Rule of thumb: Use exception handling to take action for errors that can't be prevented
by the object itself.
For example, we can't control what index value might be passed in,
but we can guarantee that myArray is never null.
Example 2: Cleaning up after handling an exception
-
Sometimes, you want certain code to execute at the end of a method.
Java supports this with the finally clause. For example, let's do an
example similar to the above, but in this case we have an array of integers, and our method
is supposed to return the sum of the first n integers in the array.
-
int sumUpTo(int n) {
-
int result = 0;
try {
-
for (int i - 0; i < n; i++)
-
result += myArray[i];
}
catch(ArrayIndexOutOfBoundsException e) { -
System.out.println("sumUpTo: Warning: array index too high");
}
finally {
-
return result; // executed regardless of whether we
catch an exception or not
}
}
In this example, what would happen if myarray was null? Would the finally clause be
executed? Yes. Normally, if you don't catch a NullPointerException, it would get thrown
up to the caller (and if not caught there, it would continue out and up the stack, but
when there's a finally clause, it gets executed regardless. And in this case, since the
finally clause has a control transfer statement (return result), the NullPointerException
wouldn't be propagated).
Example 3: Multiple catch clauses
-
Sometimes, the code inside a try block may result in more than one type of exception.
Depending on which exception arises, you may want it handled a different way. Multiple
catch clauses can be used for this -- the first one matching the exception is the one
executed. For example, consider the following method that returns a FileReader object
for a given file name.
FileReader getReaderForInputFile(String filename) throws FileNot {
-
try {
-
return new FileReader(filename);
}
catch(FileNotFoundException fnf) { -
System.out.println("File " + filename + " not found");
return null;
}
catch(Exception e) { -
System.out.println("Error opening file " + filename + ": " + e);
return null;
}
}
Example 4: Throwing your own exceptions
-
Sometimes, you may want to throw an exception to indicate that an error has occured
within your implementation. For example, suppose you have implemented a LinkedList
class, and suppose you provide a getNextElement() method that can be used to walk down
the list. However, if the list marker is already at the end, you want to throw an
exception:
public getNextElement() throws NoSuchElementException { -
if (!atEnd()) {
-
Object result = marker.next.value;
marker = marker.next;
return result;
}
else {-
throw new NoSuchElementException("past end of list");
}
}
Note: Any instance of a class that implements the Throwable interface can be thrown.
You can use any of the exceptions already defined, subclass them, or define your own.
Another way:
public getNextElement() throws NoSuchElementException { -
try {
-
Object result = marker.next.value;
marker = marker.next
return result;
}
catch (NullPointerException npe) { -
throw new NoSuchElementException("past end of list");
}
}
