SUNY Geneseo, Department of Computer Science


Exceptions

by Doug Baldwin

An "exception" is a signal that one part of a program can send to another to indicate that something unusual has happened. The unusual event is often, but not necessarily, an error of some sort. Signalling an exception is refered to as "throwing" the exception, receiving the signal and doing something about the unusual event is refered to as "handling" or "catching" the exception. Many methods defined in the Java class libraries throw exceptions, which you need to know how to catch in order to use those methods. It can also be helpful to know how to throw your own exceptions sometimes.

How an Exception is Represented

In Java, every exception is represented by an object. The Java class libraries provide many classes that represent different kinds of exception. All of these classes are descendants of class Exception.

Exception objects generally print as text that more or less describes the exception. It is often helpful to print exception objects within the code that handles them.

Throwing Exceptions

You can throw an exception by simply including a statement of the form

    throw <exception>;

in a program. Here, "<exception>" stands for any expression that produces an object from some exception class.

As an example, suppose you were writing a piece of code that needed to open a file, and that it received the file name in a string fileName. Before using this variable to open the file, you want to check that it is not null, and not an empty string. If either of these things happen, you want to throw an "IllegalArgumentException". IllegalArgumentException is one of the exception classes defined in the Java class libraries; it is typically used to create exceptions that indicate that some message has been given an incorrect argument. Here is what the code to test fileName and throw the exception might look like:

    if (  fileName == null  ||  fileName.equals("")  ) {
        throw new IllegalArgumentException( "missing file name" );
    }

As this example demonstrates, most exception classes have constructors that take a string parameter, incorporating that string into the printed form of the exception. This allows exception objects to be tailored with specific information about the error they represent. Also as suggested by this example, one often creates exception objects right in the statement that throws them.

When your program executes a "throw" statement, the program immediately stops what it is doing, and searches for a "try" block (see below) enclosing the "throw" statement. If one is found, and it has a "catch" section for this exception's class, or one of its superclasses, then control jumps to that "catch" section. Otherwise, the program looks for a suitable "try" block enclosing the place where the method containing this "throw" was invoked. Again, if one is found, control jumps to it, in effect aborting execution of the method that threw the exception. This search can continue arbitrarily far back through the method invocation history. If no handler is ever found, your program aborts.

If you throw an exception, and don't catch it in the same method, then that method's declaration has to include an indication that it can throw the exception. Method declarations that do this have the following form:

    <modifiers> <result> <name>( <... args ...> ) throws <ex1>, <ex2>, ... {
         <... method body ...>
    }

here "<modifiers>" denotes words such as "public", "static", etc. that modify a method; "<result>" is the type of value this method returns, "<name>" is the method's name, and "<... args ...>" are its parameter declarations. All of these things work exactly like they do in any method declaration, regardless of whether a method throws exceptions or not. "<ex1>", "<ex2>", etc. are the classes of exception that this method might throw. You can list as many exception classes as you need in a method declaration. For example, if the code above that checks a file name was in a method named "openFile", that method received the file name as a parameter and had no return value, and did not handle its own IllegalArgumentExceptions, then its declaration might look like this:

    public void openFile( String fileName ) throws IllegalArgumentException {
        ...
        if (  fileName == null  ||  fileName.equals("")  ) {
            throw new IllegalArgumentException( "missing file name" );
        }
        ...
    }

Note that any method that invokes a method that throws an exception must either handle that exception, or be declared as throwing it (even 'though the invoking method doesn't throw the exception directly).

Catching Exceptions

If your program throws exceptions, either because it contains "throw" statements, or because it sends messages to objects that might throw exceptions, then it should be prepared to catch those exceptions somewhere. You catch exceptions by enclosing code in a so-called "try-catch" structure. The general syntax of try-catch looks like this:

    try {
        <... statements that might throw exceptions ...>
    }
    catch ( <exception class> <name> ) {
        <... statements to handle these exceptions ...>
    }
    catch ( <exception class> <name> ) {
        <... statements to handle these exceptions ...>
    }
    ...

The statements that might throw exceptions (indicated by "<... statements that might throw exceptions ...>" above) are enclosed in a block, prefaced by the word "try". Following this block are one or more exception handlers, each consisting of a block of statements ("<... statements to handle these exceptions ...>" above) prefaced by the word "catch" and a description of the exception(s) this block handles. Descriptions of exceptions consist of the name of a class that represents the exception(s) of interest ("<exception class>" above) and a name ("<name>" above) by which the specific exception that was thrown can be referred to within the handler code (useful, e.g., for printing the exception object). A "catch" block catches any exception represented by an object of the given class, or one of its subclasses. If several "catch" blocks could handle an exception, then the one that comes first in the program is used, and later ones are ignored.

Opening files (i.e., creating so-called "streams" connected to them) provides a fairly simple example of catching exceptions -- the constructors for most streams throw exceptions from class IOException if they can't initialize their stream (as might happen, for instance, if a requested file doesn't exist). Note that IOException is defined in the standard library package java.io; you will have to import this package into files that use IOException. Here is an outline of code that creates an input stream for file "afile.txt", notifying the user if it can't make the stream:

    try {
        FileInputStream in = new FileInputStream("afile.txt");
        // code that used the stream would go here ....
    }
    catch ( IOException error ) {
        System.out.println( "Couldn't open 'afile.txt'." );
        System.out.println( error );
    }

Summary/Example

To summarize all of the syntax described above, here is a Java program that reads the first line from file "afile.txt" and prints it to the console window, if possible. The main program calls a method named "readFile" to read the line from the file; this method may throw two kinds of exception: IllegalArgumentExceptions if it is given a bad file name to read from, and IOExceptions if anything goes wrong while reading the file. The main program catches both of these exceptions, printing an appropriate error message for each. The readFile method tests its argument for validity, explicitly throwing an IllegalArgumentException if it can't be a file name. readFile may also throw IOExceptions indirectly as a result of the operations it uses to open and read from the file (don't worry if you don't understand these operations, exactly what they do isn't important in this example). Once the file is open, readFile catches any exceptions thrown while reading or closing it, so that the file can be closed before returning, if at all possible (this is an example of the sort of "cleaning up" that an exception handler can do); readFile then rethrows the exception, so that the client can handle it in a way meaningful to users (remember that low-level methods generally don't know what users will consider "meaningful", and so shouldn't try to print their own error messages or otherwise interact directly with users).

    import java.io.*;

    class ExceptionDemo {

        public static void main( String[] args ) {
            try {
                String firstLine = readFile( "afile.txt" );
                System.out.println( "The first line of afile.txt is " + firstLine );
            }
            catch ( IllegalArgumentException error ) {
                System.out.println( "Hmm, 'afile.txt' isn't a legal file name:" );
                System.out.println( error );
            }
            catch ( IOException error ) {
                System.out.println( "Unable to read afile.txt:" );
                System.out.println( error );
            }
        }

        public static String readFile( String name )
                    throws IllegalArgumentException, IOException {
            if (  name == null  ||  name.equals( "" )  ) {
                throw new IllegalArgumentException( "bad file name" );
            }
            BufferedReader in = new BufferedReader( 
                                        new FileReader(name) );
            try {
                String firstLine = in.readLine();
                in.close();
                return firstLine;
            }
            catch ( IOException error ) {
                in.close();
                throw error;
            }
        }
    }

Last Updated November 27, 2001