We’ve actually used exceptions in a few earlier examples, but have delayed talking about them until now. So, let’s talk about exceptions…

What’s an Exception?

  • Exceptions are “exceptional events” (not necessarily errors!)
  • Events that are outside the normal flow of a program
  • An exception is a subclass of class Exception (actually, Throwable, but more on that later)
  • An exception may be “thrown” automatically, or by user code
  • Can be “caught” by user code

Why Should I Care?

  • Do you check for errors when you make function calls?
    • If not, you should!
    • If you do, you probably have found that the amount of code that you have to write for error checking is often more than the main flow of the code.
  • Exceptions allow you to write the main flow of the code more simply, and separate out the nitty-gritty details of what to do when something goes wrong.
  • Exceptions also allow you to choose where to place the code to handle particular exceptions:
    • Exceptions that must be handled in the current procedure can be
    • However, some exceptions should be handled by procedures higher up the call stack.
  • Exceptions allow you to clean up your code to a considerable degree
  • Exceptions also force you to think about the exceptions that can occur, and have to deal with them.

Method Signatures & Exceptions

The first time you see mention of Java exceptions is likely to be when you are calling a method supplied in the Java class library.

For example, let’s say you want to do something relatively simple, like read a file. Here’s some code that is intended to do that:

import java.io.FileReader;

public class Test
{
    public static void main(String[] args)
    {
        // Open a file for reading
        FileReader fileReader = new FileReader("myfile.txt");
        
        // ...
    }
}

However, when you compile your program, you find that it gives you the following compilation error:

Exception java.io.FileNotFoundException must be caught, 
or it must be declared in the throws clause of this method.
        FileReader fileReader = new FileReader("myfile.txt");
                                ^

What this is telling you is that you can’t ignore the fact that the FileReader class constructor may, under some circumstances, throw a FileNotFoundException. Unless you do something, you can’t even compile the program.

Method Signatures

A class method (or constructor) may specify in its signature that it throws an exception. In the case of the FileReader class constructor, the signature looks like this:

public FileReader(String fileName) throws FileNotFoundException

A method can specify that it may throw more than one exception (only one exception may be thrown at a time!):

public void doIt() throws SomeException, SomeOtherException, YetAnotherException

In fact, a method must specify that it throws an exception if it throws that exception in its body. It must also specify that it throws an exception if it calls a method that throws that exception, or else it must catch that exception (see next section).

Java’s Catch or Specify Requirement

When a method calls a method which specifies that it throws an exception, Java requires that the calling method do one of two things:

  • Catch the exception in an exception handler, or:
  • Specify that it, in turn, throws that exception.

Classification of Exceptions

The Throwable Class Hierarchy

Here’s what the exception class hierarchy looks like:

Object
    Throwable
        Error
            <specific Errors>
        Exception
            <specific Exceptions>
            RuntimeException
                <specific RuntimeExceptions>

The Throwable Class

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or of one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause.

Throwable class instance contains a snapshot of the execution stack of its thread at the time the Throwable was created. It may also contain a message string that gives more information about the error or exception.

Errors

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a “normal” condition, is also a subclass of Error because most applications should not try to catch it.

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method, but not caught, since these errors are abnormal conditions that should never occur.

For examples of specific errors, see below.

Exceptions

The class Exception and its subclasses are a form of Throwable that indicates conditions a reasonable application might want to catch.

RuntimeExceptions

RuntimeException is a subclass of Exception which is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine.

A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method, but not caught.

Examples of runtime exceptions are:

  • Arithmetic exceptions, such as divide-by-zero, etc.
  • “Pointer” exceptions, such as trying to access an object through a null reference,
  • Indexing exceptions, such as trying to access an array element with an index that is too large or too small.
  • Etc.

Checked Exceptions

Checked Exceptions are exceptions that:

  1. Are not runtime exceptions and:
  2. Are checked by the compiler.

The compiler checks that these exceptions are caught or specified by each method, as appropriate.

Here’s a taste of what part of the Throwable class hierarchy looks like in detail:

(Package java.lang)

Object
    Throwable
        Error
            LinkageError
                ClassCircularityError
                CLassFormatError
                ExceptionInitializerError
                IncompatibleClassChangeError
                    AbstractMethodError
                    IllegalAccessError
                    InstantiationError
                    NoSuchFieldError
                    NoSuchMethodError
                NoClassDefFoundError
                UnsatisfiedLinkError
                VerifyError
            ThreadDeath
            VirtualMachineError
                InternalError
                OutOfMemoryError
                StackOverflowError
                UnknownError
        Exception
            ClassNotFoundException
            CloneNotSupportedException
            IllegalAccessException
            InstantiationException
            InterruptedException
            NoSuchFieldException
            NoSuchMethodException
            RuntimeException
                ArithmeticException
                ArrayStoreException
                ClassCastException
                IllegalArgumentException
                    IllegalThreadStateException
                    NumberFormatException
                IllegalMonitorStateException
                IndexOutOfBoundsException
                    ArrayIndexOutOfBoundsException
                    StringIndexOutOfBoundsException
                NegativeArraySizeException
                NullPointerException
                SecurityException

Catching Exceptions

The try Block

In order to catch exceptions thrown by a set of Java statements, you must first enclose the statements within a try block:

try
{
    // Java statements
}
// (Incomplete...)

The catch Block(s)

Following the try block, you may specify a number of catch blocks:

try
{
    // Java statements
}
catch(ExceptionType name)
{
    // Java statements
}
catch(ExceptionType name)
{
    // Java statements
}
// more catch blocks ...

Each catch block is an exception handler which handles the type of exception indicated. Each ExceptionType must be the name of a class that is a subclass of Throwable. The name is required, even if you don’t use it.

The Java runtime system invokes the appropriate exception handler when it is the first handler in the call stack whose ExceptionType matches the type of the exception being thrown. The system considers it to be a match if the thrown exception can legally be assigned to the handler’s ExceptionType.

Using a Single Handler to Catch Multiple Exception Types

At times, it might seem necessary to have many exception handlers, each handling a specific exception that your code throws. This can get pretty unwieldy — especially if you have more catch blocks than can fit on a page or screen.

To overcome this problem, try to use the exception hierarchy to reduce the number of catch blocks you have to specify. For example, if you’re trying to catch all exceptions that are subclasses of IOException, all you have to do is:

try
{
    // Java statements
}
catch(IOException name)
{
    // Java statements
}

If you want to catch a FileNotFoundException (a subclass of IOException) specifically, as well as catching all other IOExceptions, then you can do:

try
{
    // Java statements
}
catch(FileNotFoundException e)
{
    // Java statements
}
catch(IOException e)
{
    // Java statements
}

However, when you do such things, it introduces a catch-block ordering problem. For example, the following will not work:

try
{
    // Java statements
}
catch(IOException e)
{
    // Java statements
}
catch(FileNotFoundException e)  //  WRONG!
{
    // Java statements
}

Why not?

The technique of using class inheritance to define “families” of related exceptions is very useful:

  • To organize exceptions in a logical and consistent way, especially when a family of exceptions relates to a particular piece of functionality.
  • To reduce the number of exceptions that a method needs to specify.
  • To reduce the number of exceptions that need to be caught explicitly in a try/catch block.
  • To allow you to write code without knowing, a priori, all of the specific exceptions that may be thrown (you can use the superclass exception name as a generic name for all exceptions that extend from it, and later add additional such exceptions.) This is particularly critical when you are writing a framework to be used and extended by others.

A “catch-all” Block

Unlike C++, which has a try/catch block of the form:

try
{
    // C++ statements
}
catch(SpecificException e)
{
    // C++ statements
}
catch(...)	// Catch any other exceptions
{
    // C++ statements
}

Java has no special syntax for a catch block to catch all (remaining) exceptions. However, you can do the same thing by specifying Exception (or Throwable) as the ExceptionType in the catch block:

try
{
    // Java statements
}
catch(ExceptionType e)
{
    // Java statements
}
catch(Exception e)	// Catch any other exceptions
{
    // Java statements
}

The following is not equivalent to the above.

try
{
    // Java statements
}
catch(Exception e)
{
    // Java statements
}
catch(ExceptionType e)
{
    // Java statements
}

Why not?

The finally Block

Optionally, you may specify a finally block:

try
{
    // Java statements
}
catch(ExceptionType1 name)
{
    // Java statements
}
catch(ExceptionType2 name)
{
    // Java statements
}
// more catch blocks...
finally
{
    // Java statements
}

or:

try
{
    // Java statements
}
finally
{
    // Java statements
}

The statements in the finally block are executed when the try/catch block exits, whether normally or as a result of an exception being thrown. This is very useful — indeed, in some cases indispensable. Here’s an example:

Resource res = new Resource();
res.open();
// Do work with res ...
res.close();

This code looks fine, but doesn’t work properly when an exception gets thrown during our work with res. The close() is never executed, which leaves our resource taking up resources that should have been cleaned up. Perhaps this will result in a failure later on during the program’s execution.

As one solution to this problem, one could do something like:

Resource res = new Resource();
res.open();
try
{
    // Do work with res ...
    res.close();
}
catch(Exception e)
{
    res.close();
    throw e;
}

but this results in duplicated code, especially when you’re dealing with multiple exceptions. The finally block solution is much cleaner:

Resource res = new Resource();
res.open();
try
{
    // Do work with res ...
}
finally
{
    res.close();
}

Throwing an Exception

When you’re writing a program, you will often be confronted with the task of throwing an exception to indicate that some problem has occurred. Here are some questions you should ask when you encounter this situation:

What Should I Throw?

Well, you know that whatever you throw, it has to be a subclass of Throwable, or the compiler won’t let you do it. But, aside from that, what choices do you have?

It’s discouraged to use Errors, unless you’re doing something very special, probably with the Java Virtual Machine itself.

Here’s a list of possibilities:

  • Use an exception class that’s already provided:
    • From the java class library (for example, IllegalArgumentException, or one of many subclasses of RuntimeException)
    • From some other class library you’re using
  • Use one of your own exception classes
    • A class that already exists
    • Write a new class

You should familiarize yourself with the set of exception classes already available to you, because it’s possible that one of them would be an appropriate choice for your situation. Look at them first.

Failing that, you have to write your own exception class.

Writing Your Own Exception Class

Once you’ve made the decision to write your own exception class, you will have to decide what to use as a superclass.

First, see whether there is another exception class that it would be appropriate to extend. If there is, then use that as the superclass.

Another issue is whether you wish to have this exception be a standalone exception, or whether it should be one of a family (hierarchy) of related exceptions. Often, this kind of a family is very useful, and it can avoid lots of cases where methods have long lists of throws exceptions, and lots of lengthy catch blocks. If you decide to make a family of exceptions, or add to an existing family, then you will have to come up with the appropriate exception class to use as a superclass.

If you decide not to include the exception into an exception family, then you can use the Exception class as the superclass:

public class MyVeryOwnException extends Exception { ... }

(filling in the details represented by the above … ).

Note: It’s standard practice to append the string “Exception” to the name of every class that is a subclass (directly or indirectly) of Exception.
Similarly, if you are writing a subclass of the Error class, it should end its name with the string “Error“.

How Should I Throw It?

This is easy: Use the throw statement:

throw <exception>

Usually, the exception’s constructor is used, something like this:

throw new EmptyStackException();

or, if you wish to supply more information with the exception:

throw new EmptyStackException("This is an explanation");

However, if you have an instance of the appropriate exception class already in existence, you can throw it directly.

One case where this is common practice is where you wish to “re-throw” an exception that you just caught in an exception handler:

try
{
    // Try to do something...
}
catch (MyVeryOwnException e)
{
    // Do some cleanup...
    throw e;
}

Advice

The textbook provides some very useful and relevant advice (“tips”) on the use of exceptions (Seventh Edition, pp 576-9):

  1. Exception Handling is not supposed to replace a simple test
  2. Do not micromanage exceptions
  3. Make good use of the exception hierarchy
  4. Do not squelch exceptions
  5. When you detect an error, “tough love” works better than indulgence
  6. Propagating exceptions is not a sign of shame

All of these “tips” bear close study, and are recommended for everyday exception usage.