Exception Handling

In Java, an exception is an error that occurs during run-time. The compiler is pretty smart, as you've noticed, and it catches a lot of potential problems before run-time. If you misspell something or forget a semi-colon, it tells you. Those are compile-time errors. Your favorite is probably the "cannot resolve symbol" error. They prevent the program from compiling, so you can't even try to run it with those mistakes in it.

The compiler doesn't check for everything, however. If you accidentally try to refer to an array member with index of 10, when the highest index is actually 9, the compiler will allow it but you will get an error while the program is running. On the console you will see a message telling you that an ArrayIndexOutOfBoundsException has occurred. Or, if you do something like creating an array of buttons, but then you forget to actually construct each button, you will get a NullPointerException when you try to refer to one of those buttons. When I first started programming in Java, I got the NullPointerException all the time. I hated it!

There are also events that can occur during runtime that the compiler couldn't possible have predicted. Maybe you have provided a text field for the user to type in a number of players in a game. The idiot types in zero, and then later on when you try to deal out some cards by using the expression 52/numPlayers, you end up dividing by zero. That division operation is undefined, so the run-time environment will throw an ArithmeticException. The compiler had no way of knowing that would happen, since the value of numPlayers was set only while the program was running. As a programmer, you probably should have checked the value as soon as it was entered in the text field to make sure it was appropriate, but we really can't blame the compiler for that.

Runtime errors can also occur when you are trying to deal with files. Maybe the file you told it to look for isn't actually there. The compiler isn't going to go looking for the file in advance. And the name and path of that file might be set during run-time anyway. That's why there's something known as the FileNotFoundException. The authors of Java recognized how risky it is to try to read from a file. They knew full well that the file might not be there. That's why the class FileInputStream shows a read method with this signature:

keyhole logo
throws
public int read()

throws IOException

The keyword "throws" is a warning to the client programmer who is trying to use that read method. It's saying, we know full well that the file might not be there, which would mess everything else up. Or maybe the file's on a floppy, but the floppy drive malfunctions in some way. All sorts of things can happen, and they are grouped together in the class IOException. (IO stands for "Input/Output".) FileNotFoundException is one of the subclasses of IOException. Another subclass is EOFException, which occurs when you try to read data past the end of the file. There are many others too.

It's actually helpful that they warn you about the potential pitfalls of a method, because then you can prepare yourself to handle those exceptions when they occur. If an IOException occurs, you can respond to it, perhaps by saying, "okay stop trying to read that file, just keep going with the rest of the code." Then your program doesn't have to come crashing to a halt.

When a method declaration includes the "throws" keyword to warn you about possible exceptions, you must set up a try-catch structure to handle the exception. Your code won't compile unless you catch the exception. For example, the static method parseInt() found in class Integer has this method declaration:

public static int parseInt(String)

throws NumberFormatException

If you want to use the parseInt method (and it is a very handy little method), you must set up a try-catch structure. The try-catch structure is actually a type of flow control. Program flow always enters the try block. That's where you should put the call to the risky method (the one that might throw an exception). If that method executes successfully, program flow will never go in to the catch block. Instead, flow will continue on to whatever code follows the catch block. The catch block will just sit there unused, unless an exception is thrown somewhere during the try block. Here is a simple example of a try-catch structure from an applet with a text field that asks the user to specify the number of dice to roll:

keyhole logo
try
catch
void readTextField(){

String inputString = diceText.getText();

try {

numDice = Integer.parseInt(inputString);

}catch (NumberFormatException e) {

label.setText("That is not a number.")

}

}

The compiler certainly has no way of predicting what moronic non-numerical thing a user might type into the text field. That's why it has to be a run-time error rather than a compile-time error. In the example above, the code inside the catch block sends a message to the user indicating that the number was not typed properly.

I think "try" is a good name for the keyword: try to convert that string to an integer. See if it makes any sense. If not, we're ready for that too. The value of numDice will be reassigned only if parseInt() executes successfully without an exception. If, instead, the parseInt() method detects a problem and throws an exception, the assignment statement for numDice never gets carried out. This would make numDice stay at whatever its current value is.

The important thing about catching the exception is that it prevents the program from crashing. The user gets a message about typing a stupid thing, but has a chance to type the entry again. The text field still works and the integer variable numDice has not been corrupted by a non-integer value.

If you have tried introducing animation to an applet, you might have used the try-catch structure yourself. I used it before I understood it very much at all. I had to, because the sleep method in class Thread insisted that I catch an exception. Here is an example of exception handling from the RandomBoxes applet:

public void run(){

Thread currentThread = Thread.currentThread();

while (currentThread == animatorThread) {

drawBox();

try {

Thread.sleep(100);

}catch (InterruptedException e) {

break;

}

}

}

This is a simple animation which draws a box in a random color in a random position, and then waits for a little while before it draws the next one. The waiting is accomplished by using this static method found in class Thread:

public static void sleep(long millis)

throws InterruptedException

I can specify how many milliseconds I want the thread to cease executing. Don't do anything--just sit there. But of course, something else could happen in the meantime. If my program had multiple threads, one of the other threads might try to interrupt this one. This would cause an InterruptedException to be thrown. So that's why the sleep method is inside the try block. Try to sleep for one hundred milliseconds and see if you can do it without being interrupted. Inside the catch block, I put the break keyword to force program flow out of the while loop. This allows the code to reach the end of the run method and no more boxes will be drawn until the run method is called again.

You might be thinking, well my program doesn't have multiple threads, so why should I worry about catching the InterruptedException? Well you have no choice. Once it's declared by a throws clause in the method declaration, the compiler will force you to catch the exception. Now, it won't check to see if you have anything inside the catch block. You could leave that empty. (And by the way, don't be so confident that you know when an exception will be thrown. Even though your program has only one thread, there are other threads that the runtime environment itself is running.)

So far, we have looked at two methods--parseInt() from class Integer and sleep() from class Thread--which must be called within a try block. There are other methods that aren't forced to be inside of a try block, but you might want to put them there anyway, just to be safe. For example, in making applets, you may have noticed that the paint method gets called at times you don't expect it to. Sometimes it's called even before the graphics environment is ready to do any painting. The variable of type Graphics will have a value of null, which means if you try to use it to call a method from class Graphics, you will get that blasted NullPointerException. This code would catch the exception:

public void paint(Graphics g){

try{

g.drawRect(10,10,50,200);

}catch (Exception e){

System.out.println(e.toString());

}

}

This is a voluntary try-catch structure. The API Specification didn't tell me that I needed to provide it. It allows g to paint the rectangle if in fact g is not null. If g is null, an exception is thrown. Program flow proceeds to the catch block where it prints out a message on the console describing the exception. Then program flow continues on from wherever the paint method was called. Without catching that exception, the program would just stop. There's no reason to have it stop. We're just saying, "Fine, if the graphics aren't ready then don't bother making the rectangle. We can wait until later."

If you have other situations where you foresee some trouble, you might want to go ahead and enclose the risky code in a try block. Some methods throw exceptions in certain situations, but they're not declared in such a way that you are forced to catch the exception. For example, in class String we see this detail for the charAt() method:

public char charAt(int index)

Returns the character at the specified index. An index ranges from 0 to length() - 1. The first character of the sequence is at index 0, the next at index 1, and so on, as for array indexing.

Parameters:

index - the index of the character.

Returns:

the character at the specified index of this string. The first character is at index 0.

Throws:

IndexOutOfBoundsException - if the index argument is negative or not less than the length of this string.

You are still getting a warning about the risky nature of this method, but you are not forced to catch the exception. There is no throws clause at the top, where the method declaration is. To call this method, you could put it in a try-catch, or you could simply adopt measures to be sure that you are sending a valid argument. The same is true of the ArrayIndexOutOfBoundsException. You won't be forced to catch it. But you still want to take steps to insure that you don't cause it to be thrown.

The ArrayIndexOutOfBoundsException, along with NullPointerException, ArithmeticException, IndexOutOfBoundsException and quite a few others, all belong to a class known as RuntimeException. This is a subclass of Exception. I'm not sure why they call the subclass RuntimeException, since all the exceptions occur during run-time. Anyway, the feature that distinguishes that class is that you are not forced to catch those exceptions in code. It's voluntary, as we saw above with the charAt() method from class String. All other subclasses of Exception, such as IOException, do have to be caught. This will be important to you later on, when you learn to throw your own exceptions from your own methods. You will be able to decide whether you want to force the client programmer to catch them or not.

In the examples you've seen above, there is only one method call inside the try block. You may put as much code as you want to inside the block. If you're working with files, several of the methods you want to call may potentially throw an IOException. You may put all of those method calls into the same try block if they belong together. Similarly, the catch block can be as long as you deem necessary. Just remember that code in a catch block will only be executed if an exception is thrown.

If you put more than one line inside a try block, keep in mind that program flow will leave the block as soon as an exception is thrown. Any lines below that will never be reached. If you have something that you want to be sure to get to whether an exception occurs or not, you can use the keyword "finally" as the last part of your try-catch structure:

keyhole logo
finally

try {

}catch (Exception e) {

}finally{

}

The code in the finally block will always be executed, whether an exception is thrown or not.



Class constructors can throw exceptions also, just as methods can. Here's the constructor detail from class FileReader:

public FileReader(String fileName)

throws FileNotFoundException

Creates a new FileReader, given the name of the file to read from.


Parameters:

fileName - the name of the file to read from

Throws:

FileNotFoundException - if the specified file is not found

So, if you wanted to make a variable of type FileReader, you would have to call its class constructor inside of a try block. Here is a code fragment from the ARHS Java Editor, when the user has asked to load a saved file:

instructionLabel.setText("now loading "+ fileName);

instructionLabel.paintImmediately(0,0,instructionLabel.getWidth(),instructionLabel.getHeight());

try{

FileReader theReader = new FileReader(newFile);

char[]characterArray = new char[(int)newFile.length()];

theReader.read(characterArray);

String documentString = new String(characterArray);

...

The try block for that method goes on for many more lines after this. You can see that the first line in the block is a call to the class constructor for FileReader, and we know that call must be within a try block. Then, two lines below, the variable named theReader calls the read() method from class FileReader. This method call is also required to be within a try block. There are a lot of other code statements that follow. They aren't required to be within a try block, but they're in this one, because they need access to some of the variables that were defined within the try block. Like any other form of flow control, the try block restricts variable scope. The variable documentString, for example, is declared within the try block and its scope is limited to the end of that block. Other code statements are inside the try block because I don't want them to be executed unless the call to the FileReader constructor and the read method call were successful. If one of those procedures throws an exception the rest of the try block will be ignored.



Read more about exceptions: David Eck's textbook
Sun Java tutorial
Bruce Eckel's textbook--
a little
a whole chapter