Lesson 5: 29 April: Chapters 9-10

Error Handling

Definition: exception is "an event that occurs during the execution of a program that disrupts the normal flow of instructions." (tutorial)

In other words, it's something that might happen which is not desired.

This could be a hardware failure, e.g. disk crash or network outage.

It could be a mistake in program logic, e.g. reading beyond the end of an array.

A well-written program handles many possible failures gracefully, i.e. doesn't just crash.

To start, here's an example of a non-Java way of handling exceptions (pseudo-code not a real language):

The main program flow is this:

<7.1>

readFile {

open the file;

determine its size;

allocate that much memory;

read the file into memory;

close the file;

}

Here is the full code:

<7.2>

errorCodeType readFile {

initialize errorCode = 0;

open the file;

if (theFileIsOpen) {

determine the length of the file;

if (gotTheFileLength) {

allocate that much memory;

if (gotEnoughMemory) {

read the file into memory;

if (readFailed) {

errorCode = -1;

}

} else {

errorCode = -2;

}

} else {

errorCode = -3;

}

close the file;

if (theFileDidntClose && errorCode == 0) {

errorCode = -4;

} else {

errorCode = errorCode and -4;

}

} else {

errorCode = -5;

}

return errorCode;

}

Obviously there is so much exception handling code here that it is hard to see the main logic. That is a serious code maintenance problem.

It's also so complicated that it will be hard to get it right

Java supports an exception mechanism that separates the exception handling from the main program flow. The above example might be written:

<7.3>

readFile {

try {

open the file;

determine its size;

allocate that much memory;

read the file into memory;

close the file;

} catch (fileOpenFailed) {

doSomething;

} catch (sizeDeterminationFailed) {

doSomething;

} catch (memoryAllocationFailed) {

doSomething;

} catch (readFailed) {

doSomething;

} catch (fileCloseFailed) {

doSomething;

}

}

Note how the main program flow is preserved intact.

A second advantage is automatic propagation up the call stack. The tutorial has this example of a "traditional" way to propagate errors:

<7.4>

method1 {

errorCodeType error;

error = call method2;

if (error)

doErrorProcessing;

else

proceed;

}

errorCodeType method2 {

errorCodeType error;

error = call method3;

if (error)

return error;

else

proceed;

}

errorCodeType method3 {

errorCodeType error;

error = call readFile;

if (error)

return error;

else

proceed;

}

Note how each method is responsible for propagating the error up the stack, even if it can't actually do anything about the error.

In Java, the exception will automatically propagate up the stack until it finds a handler that can deal with it:

<7.5>

method1 {

try {

call method2;

} catch (exception) {

doErrorProcessing;

}

}

method2 throws exception {

call method3;

}

method3 throws exception {

call readFile;

}

Note how only method1 has to do anything about the exception even if it is generated way down in the readFile.

Also note that the methods which don't handle the exception still need to declare throws exception.

What's nice about this is exceptions can be handled by the method that knows how to handle them, and other methods can basically ignore them.

Java exceptions are objects. They all derive from the throwable class.

(Show javadoc for throwable)

All throwable objects have a few useful things:

A string message describing what went wrong

A stack trace that tells you where in the code the exception was thrown

There is a whole class hierarchy descending from throwable.

(Show some examples from this hierarchy)

Catch or Specify requirement

If you try to compile the following class

<7.6>

public class InputFile {

private FileReader in;

public InputFile(String filename) {

in = new FileReader(filename);

}

}

You will get the following compile error:

<7.7>

InputFile.java:8: Warning: Exception java.io.FileNotFoundException must be caught,

or it must be declared in throws clause of this method.

in = new FileReader(filename);

That's because the FileReader constructor throws a FileNotFoundException

Compiler expects you either to catch it, like this:

<7.8>

public class InputFile {

private FileReader in;

public InputFile(String filename) {

try {

in = new FileReader(filename);

} catch (java.io.FileNotFoundException e) {

//print out the stack so we know where it happened

e.printStackTrace();

}

}

}

Or you can catch any superclass of FileNotFoundException:

<7.9>

public class InputFile {

private FileReader in;

public InputFile(String filename) {

try {

in = new FileReader(filename);

} catch (java.lang.Exception e) {

//print out the stack so we know where it happened

e.printStackTrace();

}

}

}

Or, if we don't catch it ourselves, we need to declare a throws clause in our method declaration:

<7.10>

public class InputFile {

private FileReader in;

public InputFile(String filename) throws java.io.FileNotFoundException {

in = new FileReader(filename);

}

}

Again, we can throw any superclass of FileNotFoundException:

<7.11>

public class InputFile {

private FileReader in;

public InputFile(String filename) throws Exception {

in = new FileReader(filename);

}

}

This is so users of the class know what to expect, and so the compiler can force it to be caught somewhere.

The "exception" to the rule: RuntimeExceptions. These are common programming errors that can occur at any time, for example:

NullPointerException: o.callMethod() when o is null.

ArithmeticException: includes divide by 0.

IndexOutOfBoundsException: accessed a nonexistent array element

In these cases, the compiler does not force you to catch or declare throws. The thinking was, they are just too ubiquitous.

You've seen try-catch. There is one more type of block called finally which is for doing any cleanup that might be necessary. Finally is always called whether or not an exception is thrown.

<7.12>

public void writeList() {

PrintWriter out = null;

try {

System.out.println("Entering try statement");

out = new PrintWriter(

new FileWriter("OutFile.txt"));

for (int i = 0; i < size; i++)

out.println("Value at: " + i + " = " + victor.elementAt(i));

} catch (ArrayIndexOutOfBoundsException e) {

System.err.println("Caught ArrayIndexOutOfBoundsException: " + e.getMessage());

} catch (IOException e) {

System.err.println("Caught IOException: " + e.getMessage());

} finally {

if (out != null) {

System.out.println("Closing PrintWriter");

out.close();

} else {

System.out.println("PrintWriter not open");

}

}

}

I'm going to go against orthodoxy a here. The Java gurus say you should catch all kinds of different exceptions in a series as shown above.

But I find this tedious and not thorough enough: what if a RuntimeException occurred such as a NullPointerException?

Some of my exception handlers just look like this:

<7.13>

public void writeList() {

PrintWriter out = null;

try {

System.out.println("Entering try statement");

out = new PrintWriter(

new FileWriter("OutFile.txt"));

for (int i = 0; i < size; i++)

out.println("Value at: " + i + " = " + victor.elementAt(i));

} catch (Exception e) {

//catch any kind of exception that might have occurred!

//(I don't really care which kind it was)

//print stack to console so we can analyze the problem later

e.printStackTrace();

// notify the user something went wrong

JOptionPane.showMessageDialog(null, "Program error: " + e.getMessage());

}

}

}Throwing exceptions

All these exceptions that we can catch have to be created somewhere. That is called the throw statement.

A lot of Java API classes throw exceptions by design, so you will probably get more experience catching than throwing.

Here's an example

<7.14>

public Object pop() throws EmptyStackException {

Object obj;

if (size == 0)

throw new EmptyStackException();

obj = objectAt(size - 1);

setObjectAt(size - 1, null);

size--;

return obj;

}

Only a Throwable object (including subclasses) can be thrown.

You should never use throw as a kind of goto to get from one place to another-- only when something goes wrong.

Creating your own exception classes

You can roll your own just by extending java.lang.Exception or one of its subclasses.

Some people might find that useful, for me it's just tedious. I've got more important things to do than create new exception classes.

For me, throw new Exception(msg) usually does the trick.