While catching the Throwable
, Exception
, Error
and RuntimeException
exceptions is bad, throwing them is even worse.
The basic problem is that when your application needs to handle exceptions, the presence of the top level exceptions make it hard to discriminate between different error conditions. For example
try {
InputStream is = new FileInputStream(someFile); // could throw IOException
...
if (somethingBad) {
throw new Exception(); // WRONG
}
} catch (IOException ex) {
System.err.println("cannot open ...");
} catch (Exception ex) {
System.err.println("something bad happened"); // WRONG
}
The problem is that because we threw an Exception
instance, we are forced to catch it. However as described in another example, catching Exception
is bad. In this situation, it becomes difficult to discriminate between the "expected" case of an Exception
that gets thrown if somethingBad
is true
, and the unexpected case where we actually catch an unchecked exception such as NullPointerException
.
If the top-level exception is allowed to propagate, we run into other problems:
Exception
and Throwable
we also need to add these exceptions to the throws
clause of methods if we want the exception to propagate. This is problematic, as described below.In short, don't throw these exceptions. Throw a more specific exception that more closely describes the "exceptional event" that has happened. If you need to, define and use a custom exception class.
It is tempting to replace a long list of thrown exceptions in a method's throws
clause with Exception
or even `Throwable. This is a bad idea:
Exception
.Exception
properly is difficult. It is hard to know what actual exceptions may be caught, and if you don't know what could be caught, it is hard to know what recovery strategy is appropriate.Throwable
is even harder, since now you also have to cope with potential failures that should never be recovered from.This advice means that certain other patterns should be avoided. For example:
try {
doSomething();
} catch (Exception ex) {
report(ex);
throw ex;
}
The above attempts to log all exceptions as they pass, without definitively handling them. Unfortunately, prior to Java 7, the throw ex;
statement caused the compiler to think that any Exception
could be thrown. That could force you to declare the enclosing method as throws Exception
. From Java 7 onwards, the compiler knows that the set of exceptions that could be (re-thrown) there is smaller.