We have to resign ourselves to the fact that errors are going to occur in our programs. The important thing, of course, is how our programs react to those errors. In particular, how are they to be handled, and by whom (i.e., by which part of the program)? Ultimately, the question is this: Can the program recover from the error, or should it simply be allowed to die?

What is an exception?

What is an exception handler?

An exception handler is a piece of code that is automatically invoked when an exception occurs, and may allow the programmer to fix (or at least deal with in some way) an exception when it occurs.

In essence, exception handling provides a structured means for the programmer to "catch" runtime errors in a program, and make them return meaningful information about themselves. The programmer can then choose to ignore the information, or, which is usually the better alternative, to perform certain actions before permitting the program to carry on or terminate.

What is the syntax for dealing with exceptions?

Here is the syntax for "trying" some code, and "catching" two exceptions that might get "thrown" if either "exceptional circumstance" occurs while you're doing so:

try
{
    // Some code that might cause an exception to be thrown
}
catch (ExceptionType1 e1)
{
    // Do whatever should be done if e1 occurs
}
catch (ExceptionType2 e2)
{
    // Do whatever should be done if e2 occurs
}
Note that in a sequence of "catches" like this, the most "general" one should be last.

Don't use exception handling as a "break statement on steroids". Exceptions should be thrown only in "exceptional" circumstances. For example, if you are reading from a file and "expect" to hit the end-of-file, this is not an exceptional circumstance and should not, therefore, be handled by throwing an EOFException.

How do you create and throw your own exceptions?

When, why and how do you "re-throw" an exception?

What is a finally block and when would you use one?

Best Practices in Exception Handling (from Sun's site on March 17, 2004)

Exception handling is a built-in aspect of the Java programming language. The concept of handling error cases with exceptions is designed to make code more reliable and easier to maintain and read. This tip looks at some best practices for dealing with and handling exceptions.

As a quick refresher, here's the full construct for exception handling:

try
{

    // code which could potentially
    // throw an exception

}
catch (TheException1 e)
{

    // code to handle exceptional condition

}
catch (TheException2 e)
{

    // code to handle next exceptional condition

}
finally
{

// code to run whether an exceptional
// condition happened or not

}

Basically, code that might throw an exception goes within a try block. A specific catch block code runs only if the exception identified for it occurs (or its subclass). However, the code in the finally block runs under all conditions -- this even includes the case when the try-catch code calls return to exit out of the current method.

The try block is required. Additionally, one of the two blocks, catch or finally, must be present, or the compiler will complain.

Given that you can have multiple catch clauses, the system finds the first one that matches the exceptional condition. So, if you have a catch block for an IOException, and a FileNotFoundException happens, this is caught by the general IOException catch clause.

This suggests a best practice for exception handling: Always work with as specific an exception as possible. For instance, if you create a method that could generate a FileNotFoundException, don't declare that method to throw an IOException. If you do, it forces the caller to handle all IOExceptions that are possible, not just FileNotFoundExceptions. You shouldn't get too specific though. In particular, the declared exception thrown by a method should never reveal implementation details.

Another best practice is try to avoid empty catch blocks. In other words, don't ignore exceptions when they happen.

// This code is bad

try
{
...
}
catch (AnException e)
{
}

The designers of system libraries throw exceptions for a reason: to tell you some exceptional condition happened. It's good practice to do something in response, even simply logging the problem. For classes you design, only declare methods to throw exceptions where you want the caller to deal with a problem. If you want to ignore an exception, perhaps because of an automatic retry that happens every minute or two, simply place a comment in the catch block to explicitly say why the exceptional condition is ignored.

// Better
try
{
...
}
catch (AnException ignored)
{

// System automatically retries every minute
// Nothing to really do until retry

}

One very important practice related to exceptions thrown from methods is documentation. There is a javadoc tag for documenting which exceptions are thrown by a method. It's @throws. Best practices call for all checked exceptions to be documented. Common runtime exceptions should be documented too. For example, in the following method declaration, both the thrown exception and why the throwable exception could be thrown are documented. Don't just have the @throws ExceptionName bit. That doesn't add any more than the method declaration alone.

/**
* Loads the class
*
* @param  name
*         Class name
*
* @return  Resulting Class object
*
* @throws  ClassNotFoundException
*          If class not found
*/
public Class loadClass(String name)
throws ClassNotFoundException
{
    ...
}

For runtime exceptions, an exception here is the parseInt method of Integer. This can throw a NumberFormatException (this is declared as part of the method declaration). However, because this is a RuntimeException, it doesn't have to be declared.

Not only don't runtime exceptions have to be declared, but neither do they have to be checked. For instance, every array access can throw an ArrayIndexOutOfBoundsException. It is not good practice to wrap all array access code within a try-catch block. Doing that makes code difficult to read and maintain.

// This is bad, don't do it:
try
{
    for (int i=0, n=args.length; i<n; i++)
    {
        System.out.println(args[i]);
    }
} catch (ArrayIndexOutOfBoundsException e)
{
    System.err.println("Ooops");
}

For recoverable conditions, it is good practice to have catch clauses with actions involved. These are typically checked exceptions, although it's also possible with runtime exceptions. Case in point: the previously mentioned parseInt method of Integer. If you are validating user input with the parseInt method, that is certainly a recoverable operation (assuming the user is still available).

While it is possible to create your own custom exceptions, more typically, you can reuse the system exception classes. These are usually sufficient for most needs, though not for every need.

Runtime exceptions that you might use include the following:

NullPointerException
Tends to be encountered more by accident than by design. If this commonly happens to you, you might think of peer reviews.
IllegalArgumentException
Often encountered when validating arguments to a method. In some cases, they might result from the typical explicit checks at the start of a method. In other cases, they could happen after various operations are performed. Subclasses include NumberFormatException and PatternSyntaxException (for regular expressions).
IndexOutOfBoundsException
The ArrayIndexOutOfBoundsException class is a subclass, so is StringIndexOutOfBoundsException. You can use these in your classes and methods to check for passing in an integer beyond the end (or before the beginning) of your data structure.
UnsupportedOperationException
Typically used by the Collections Framework to indicate that a requested operation is not supported. You can use it to indicate similar usage problems.
Reusing existing exception classes allows developers to keep their codebase smaller and also take advantage of the familiarity they have with the existing exception class hierarchy.

The final aspect of exception handling to explore is exception chaining. Introduced with the 1.4 release of the Java 2 Platform, Standard Edition, exception chaining allows you to attach the underlying cause of a problem to the thrown exception. Chaining is not usually meant for an end user to see. Instead, it allows the person debugging a problem to see what caused the underlying problem.

For instance, in the database world, specifically in the JDBC libraries, it is common to have catch clauses for SQLException. Even prior to exception chaining, the SQLException clause had a getNextException method which allowed chaining of exceptions. To find out all the underlying causes of the SQLException, you could have a loop that looks something like the following:

try
{

    // JDBC code

}
catch (SQLException ex)
{
    while (ex != null)
    {
        System.err.println(
        "SQLState: " + ex.getSQLState());
        System.err.println(
        "Message:  " + ex.getMessage());
        System.err.println(
        "Vendor:   " + ex.getErrorCode());
        System.err.println("-----");
        ex = ex.getNextException();
    }
}

Instead of using the JDBC version of chaining, the standard approach is now the one shown below. Instead of printing the SQL State, message, and error code, just the exception type and message are displayed.

try
{

    // Normal code

}
catch (AnException ex)
{
    while (ex != null)
    {
        System.err.println(
        "Type: " + ex.getClass().getName());
        System.err.println(
        "Message: " + ex.getMessage());
        System.err.println("-----");
        ex = ex.getNextException();
    }
}

There is certainly much more that can be done with exceptions. Hopefully, the practices shown here should help get you started toward better understanding and usage.