Java and many modern dynamic languages have the handy ability that you can try to recover almost any kind of error, even null pointer errors.
This of course doesn’t mean you should actually do it.
I see a lot of people using
try {
…
catch (Exception e) {
//ignore the error
}
Especially on the outer look of a process they want to live for a long time.
This isn’t a good idea for a couple of reasons:
- The only sort of exceptions that you really want to catch in java are checked exceptions, and the compiler will actually *force* you to catch these exceptions. Unchecked exceptions, most of which are descended from java.lang.RuntimeException, indicate that something has gone horribly and unrecoverably wrong and that it is time for you application to die.
If you want your service to live on… just make it restart automically when it dies. This is not that hard. If instead you try to ignore errors that aren’t meant to be recovered from, it’s likely that the next request to your service will fail with a similar error, and again ignore it. Then your service will become a black hole which requests go into, but never return.
What’s worse, you won’t get any feeback about it, since unless your service actually *dies* errors are likely to go unnoticed in tests. - catch(Exception e) doesn’t actually do what you think it does. If you use this construct a lot, you probably think it catches all exceptions. It does not. There are *at least* three primary kinds of exceptions:
- Checked exceptions: descended from Exception, but not RuntimeException. These are meant to be handled by the caller and are part of the function interface through the throw clause. IOException is a common example.
- Unchecked exceptions: descended from RuntimeException, which is confusingly descended from Exception. NullPointerException is the most common example. Can be caught with “catch(Exception e)” but should not be caught that way (more on this later).
- Errors: Descended from Error. AssertionError is an example. Cannot be caught with a catch(Exception e)
I said at least because you can come up with your own heirarchy of exceptions by implemting Throwable. The correct way to catch *anything* is with a “catch (Throwable t)”. Of course, you should only do this if you rethrow t because otherwise you can leave your program in an invalid state. Probably this is only useful for logging then rethrowing, or debugging.
- Now, suppose you want to catch all unchecked exceptions, but not errors. Unchecked exceptions aren’t really meant to be handled, but Errors are meant to kill your application *for realz*.
“try {
f();
catch(Exception e) {}”
might seem obvious, but it munges together handling of checked exceptions *and* unchecked exceptions in the same code. If f can throw a checked exception, your compiler will *force* you to handle it, so handle your unchecked exceptions with:
“catch(RuntimeException e)”
Then if you also need to handle checked exceptions add additonal checked exception catch clauses for the *specific types* the compiler forces you to handle. For instance, the compiler might tell you to handle IOException:
“try {
f();
}
catch(IOException e) {
//you should actually be able to recover from this here
}
catch(RuntimeException) {
//you probably can’t recover from this. At best you can try to ignore it.
}”
Even better, you should try to add specialized handles for subtypes of your IOException that you know, perhaps from documentation or experience, that f() might throw.
“try {
f();
} catch(FileNotFoundException e) {
// handle the exception in a way specific to the file not being found.
} catch(IOException e) {
// handle the more general case if you can, or rethrow it in a runtime exception like so:
// throw new RuntimeException(“I don’t know how to handle this exception!”, e);
} catch(RuntimeException e) {
// Probably should just log and rethrow.
}”
I’m not a big fan of how the Java exception type system works. Having exceptions that don’t descend from Exception, or having exceptions that aren’t called exceptions just seems stupid to me. Still, at least they work better than C++ exceptions, where any type can be thrown (yes I know that you can *handle* any time as well, but still…), and exception specifications don’t force you to handle the exception at compile time (which is why no one uses them).