In this article I discuss some of the exceptions best practices. I’m focusing on exception handling only, on how to avoid a situation where a system fails without clues of what happened or where the code is tangled with ugly exceptions handling without any reason.
I won’t talk about the finally clause, which is covered extensively elsewhere.
Causes of the exception
There are different situations that may cause an exception to be thrown:
- Client error: A method invocation is not valid because there is some kind of error in the client code or in the data provided as parameters.
- Resource failure: The exception was caused due to a resource failure, like a fallen network connection, or a fallen database.
- Programming error: The exception shouldn't be never thrown, but is due to a programming error. The most classical example of this is NullPointerException.
Do not swallow exceptions
When an exception occurs, you (the programmer) can be in one of two situations: Most of the times, you won’t know how to solve the problem, but sometimes you may know what to do.
An example of the programmer knowing what to do could be the situation where he tries to update a row in a DB and, in case the row does not exist, insert it as a new row:
Example: It is ok the catch an exception when you know what to do to solve it
1
2
3
4
5
6
7
8
9
try {
update(data);
}catch(RowNotFoundException e){
insert(data);
}
Whenever you don’t know what to do with the exception, you shouldn’t really catch it. If you did so no exception would be thrown by the method, the client code would never know that something went wrong.
Common mistakes regarding this are:
- Empty catch blocks
- Catch blocks with only "e.printStackTrace()" or "logger.warn(e)"
Examples of bad practices
1
2
3
4
5
6
7
8
9
try {
...
} catch(WhateverException e) {
e.printStackTrace();
}
1
2
3
4
5
try {
...
} catch(WhateverException e) {}
1
2
3
4
5
6
7
8
9
10
11
try {
...
return something;
} catch(WhateverException e) {
return null;
}
Use standard exceptions when possible
Java already defines a bunch of common exceptions that can (and should) be used instead of creating a new exception class every time. Some common examples are:
- IllegalStateException: The invocation of the operation is not valid because of the current state of the object.
- IllegalArgumentException: One of the arguments of the method call is illegal. Example: An int argument must be >= 0.
Always use unchecked exceptions
Java checked exceptions (those that are subclass of Exception) where intended to be used when the client code finding the exception knows how to handle the exception. Checked exceptions force the client programmer to catch them or declare them in his throws clause. On the other hand, unchecked exceptions (those inheriting from RuntimeException) don’t force the programmer to do anything, as they are re-thrown transparently if the programmer decides not to catch them.
Most of the times, the client code can only report the exception to the end user somehow, and let her decide what to do. So, most of the times, the programmer can only throw the exception again until some class at the top of the call stack handles it. If the exception is a checked exception, then, the programmer needs to add it to the throws clause of the method, which makes it less easy to understand.
Therefore, it is much better to use unchecked exceptions.
If necessary, translate checked exceptions as unchecked exceptions
If you use a library that throws checked exceptions you can’t handle, you must propagate those exceptions. How can you use unchecked exceptions in such a situation? You just translate them.
To do so, create a new RuntimeException (or any subclass of it) and pass the checked exception as an argument to the constructor. Since Java 5, when, finally, the stack trace of the exception is printed, the stack trace of the nested exception is printed as well.
Example:
1
2
3
4
5
6
7
8
9
try{
//some i/o code that throws IOException
}catch(IOException e){
throw new RuntimeException("I/O exception while doing whatever", e);
}
Use a single exception reporting point
If you read until here, you know I don’t want you to swallow exceptions. Ok, so they’ll be propagated… to where?
If you are programming a web application, they could be propagated until they reach the application server. But then, the client, usually a human user, will receive an ugly HTTP 500 error page.
The solution is to use a single exception reporting point, a last resort before the exception hits the application server. That point should catch the exception, log it for debugging and diagnosis purposes and, usually, report that something went wrong to the client.
Reporting to the client, in the common case where the client is a human user, involves showing an error screen. If your client is another computer system (you are probably programming some kind of web services), then you need to translate the exception as an error response in whatever communication protocol you are using.