Course Links

Exercises

Resources

External

Introduction

This tutorial is designed to teach you a bit about exceptions and exception handling. Basically, exceptions are a flexible mechanism for handling error conditions that may or may not occur during the execution of some piece of a program. If the error does not occur, then the program executes normally. If an error does occur, then normal execution is terminated and a special error handler executes to deal with the unusual situation. Normal execution eventually resumes with code outside the error-prone block.

Using exceptions involves three keywords: try, catch, and throw. The first, try, identifies the code to be executed under normal circumstances. The second, catch, specifies error handling code to be activated in case of an error. Finally, throw signals the presence of an error, triggering immediate termination of the try block and transfer of control to the catch block.

The catch block (also called the exception handler) that executes need not be in the same method that threw the exception in the first place. If no appropriate catch block can be found in the current method, then its stack frame will be discarded and the next frame on the stack will be searched for a matching catch block. Matches are determined by the catch's single exception argument -- if the actual exception thrown is a subtype of the catch's argument, then the handler will execute. If not, then the search for an appropriate handler continues.

Catching Exceptions

Begin by looking at a simple demo program. As written, it will trigger an ArithmeticException (division by zero). Compile it and run, so you can see what happens. Which print statements execute? Which do not?

Now let's catch the exception. Replace the body of the main method with the following:

	System.out.println("Entering main.");
	try {
	    method1();
	} catch (Exception e) {
	    System.out.println("Caught Exception in main: " + e);
	}
	System.out.println("Exiting main.");
Run the program again and see what happens. Where does execution resume after the exception is thrown?

What if we add another handler in method1? Try the following:

    public static void method1() {
	System.out.println("Entering method1.");
	try {
	    method2();
	} catch (Exception e) {
	    System.out.println("Caught Exception in method1: "+e);
	}
	System.out.println("Exiting method1.");
    }
Now where does execution resume after the exception is thrown?

So we have only caught generic exceptions. We could be more precise and catch just an ArithmeticException. Change the type of the exception in method1 to ArithmeticException. Does anything change about the execution? If we add a new exception handler in method2 for generic Exception, which one would run? (Try it if you are not sure.)

One final change: a handler can be triggered to take some action, but rethrow the exception to an outer level. Modify the body of method1 to the following and observe what happens (you may need to disable any handlers in method2, if you added them):

    public static void method1() {
	System.out.println("Entering method1.");
	try {
	    method2();
	} catch (Exception e) {
	    System.out.println("Caught Exception in method: "+e);
            throw e;
	}
	System.out.println("Exiting method1.");
    }

Checked Exceptions

Some exceptions are considered predictable, and therefore Java requires that programs which might produce them also provide an appropriate handler. Add the following line tomethod3 somewhere:

	Thread.sleep(25);
The code will no longer compile, because this call is known to potentially throw a InterruptedException and we have not allowed for that possibility with a corresponding handler. Alternatively, we could indicate that this method will not contain a handler, and its caller should instead. (In other words, we are passing the buck to some other part of the program.) Do this by adding throws InterruptedException to the call signature.
    public static void method3() throws InterruptedException {
The next level of the program also faces the same choice: either handle the exception, or pass the buck onwards. Alter your program again so that it will compile. You can either add an appropriate handler in method2, or continue passing the exception outwards. When it reaches main, the generic handler already there will deal with it.

How should you decide whether to handle an exception or pass it along? As a general rule, you should handle the exception as soon as you have the necessary information to do so. It can even happen in the same method that generates the exception.

Writing Exceptions

Exceptions are objects like any other in Java. That means you can define your own exceptions. If they inherit from class Exception they will be checked by the compiler. If they inherit from RuntimeException they will not.

Consider the class Box. It simulates a physical container with limited capacity. At the moment, there is nothing to stop a user from adding more items to the box than it can actually hold. We would like to prevent that by throwing an exception when such an attempt is made.

Since there is no preexisting exception type that seems appropriate for this situation, we will write our own. Call it BoxFullException, and put it in its own separate java file. It should extend either Exception or RuntimeException -- which do you prefer? You may if you want add extra fields to the exception to record details of the incident -- the box that was too full and the item that was to be added, for example.

When you have defined your new exception class, put code in the add method that will create and throw an exception when an item to be added would cause the box to exceed its capacity.

To Do

Look at the class PackItems, designed to be used with the class Box above. As written it contains multiple lines that can generate exceptions. Your job for this exercise is not to fix those lines, but to make the program runnable by wrapping them all in exception handlers that take an appropriate action. For example, if the program is adding an item to a box that is already full, switch to the next box. You should not alter any of the existing lines -- just add exception handlers to make the program do what you want. (It is ok to put some of the existing lines inside a try block so long as you don't change them.) When finished your program should add each item to a box, and print out the full list of items in boxes, without errors and without losing any items.

For Fun

If you think you understand everything there is to know about exceptions now, check out this cartoon from the webcomic XKCD.