Catching Exceptions
Home ] Up ] Exceptions: What and Why? ] Method Signatures and Exceptions ] Classification of Exceptions ] [ Catching Exceptions ] Throwing an Exception ] Exception Hierarchies ]

 

 

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
}

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)
{
    // 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 parrticularly 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 for catching all exceptions. However, you can do the same thing by specifying Exception (or Throwable) as the ExceptionType in the catch block:

try
{
    // Java statements
}
catch(SpecificException 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(SpecificException e)
{
    // Java statements
}

Why not?

The finally Block

Optionally, you may specify a finally block:

try
{
    // Java statements
}
catch(ExceptionType name)
{
    // Java statements
}
catch(ExceptionType 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();
}
 
The page was last updated February 19, 2008