Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Breaking Java exception-handling rules is easy

Did you know that any Java method can throw arbitrary checked exceptions without declaring them?

  • Print
  • Feedback

February 28, 2003

Q I am calling an external method in my application and want to intercept any exceptions it can possibly throw. Should I catch java.lang.Exception?

A Handling all runtime exceptions and checked exceptions declared by a given method is not sufficient to build the ultimate defense against external failures. In this Java Q&A installment, I show how any Java method can throw an arbitrary exception—checked or unchecked—with relative ease and without employing any native code.

You may have read the recent JavaWorld article, "Java Tip 134: When Catching Exceptions, Don't Cast Your Net Too Wide" by Dave Schweisguth. That excellent article warned against catching java.lang.Exception or java.lang.Throwable as a bad, catch-all practice. Catching the most specific exception you can is important for code maintainability and future refactoring. However, this rule must be bent under special circumstances. Particularly, if you intend not to crash and retain your data structures' exception safety in the presence of callouts to external, potentially hostile code, you must intercept java.lang.Throwable regardless of the actual exception type being thrown.

For a specific example, imagine you have a server application that loads implementations of this interface:

public interface IFoo
{
    /**
     * This method can't throw any checked exceptions...or can it?
     */
    void bar ();
} // End of interface


For argument's sake, let's say you host such a server somewhere, and various IFoo implementations could be uploaded from external sources. You have coded

        try
        {            
            IFoo foo = ... // get an IFoo implementation
            foo.bar ();
        }
        catch (RuntimeException ioe)
        {
            // Handle 'ioe' ...
        }
        catch (Error e)
        {
            // Handle or re-throw 'e' ...
        }
and think you have handled all the possibilities here. You don't need to add any more catch blocks, say, for java.io.IOException because, well, no IFoo implementation could throw it from IFoo.bar(), right? (In fact, as Schweisguth discussed, even if you tried to add a catch for java.io.IOException, the compiler will likely reject it as an unreachable catch block.)

Wrong. The bar() implementation in the EvilFoo class I wrote to prove a point will happily throw anything you passed to the class constructor (saved in m_throwthis):

    public void bar ()
    {
        EvilThrow.throwThrowable (m_throwthis);
    }


Run the Main class from the download associated with this article:

public class Main
{
    public static void main (final String[] args)
    {
        // This try/catch block appears to intercept all exceptions that
        // IFoo.bar() can throw; however, this is not true
        try
        {            
            IFoo foo = new EvilFoo (new java.io.IOException ("SURPRISE!"));
            foo.bar ();
        }
        catch (RuntimeException ioe)
        {
            // Ignore ioe
        }
        catch (Error e)
        {
            // Ignore e
        }
    }
} // End of class


You will see an instance of java.io.IOException getting thrown from bar() and not getting caught by any catch blocks:

>java -cp classes Main
Exception in thread "main" java.io.IOException: SURPRISE!
        at Main.main(Main.java:23)


What happened here?

The key observation is that the usual Java rules for checked exceptions are enforced at compile time only. At runtime, a JVM will not ensure that the exception thrown by a method actually matches the set of exceptions declared by the method in its throws declaration (see the JVM specification). Since it is the responsibility of the calling method to intercept and handle all checked exceptions thrown from a called method, anything not declared by the called method will simply not get noticed and will pass up the call stack. This behavior may change in future JVMs, but it has not at the time of this writing.

  • Print
  • Feedback

Resources