|
|
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
February 28, 2003
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?
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' ...
}
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)
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.