Professional Documents
Culture Documents
One of the problems with exception handling is knowing when and how to
use it. In this article, I will cover some of the best practices for exception
handling. I will also summarize the recent debate about the use of checked
exceptions.
This method is a blank one; it does not have any code in it. How can a blank
method throw exceptions? Java does not stop you from doing this. Recently, I
came across similar code where the method was declared to throw
exceptions, but there was no code that actually generated that exception.
When I asked the programmer, he replied "I know, it is corrupting the API,
but I am used to doing it and it works."
Broadly speaking, there are three different situations that cause exceptions
to be thrown:
I have seen heavy use of checked exceptions and minimal use of unchecked
exceptions. Recently, there has been a hot debate in the Java community
regarding checked exceptions and their true value. The debate stems from
fact that Java seems to be the first mainstream OO language with checked
exceptions. C++ and C# do not have checked exceptions at all; all
exceptions in these languages are unchecked.
The method getAllAccounts() throws two checked exceptions. The client of this
method has to explicitly deal with the implementation-specific exceptions,
even if it has no idea what file or database call has failed within
getAllAccounts(), or has no business providing filesystem or database logic.
Thus, the exception handling forces an inappropriately tight coupling
between the method and its callers.
Best Practices for Designing the API
Having said all of this, let us now talk about how to design an API that throws
exceptions properly.
If the client can take some alternate action to recover from the exception,
make it a checked exception. If the client cannot do anything useful, then
make the exception unchecked. By useful, I mean taking steps to recover
from the exception and not just logging the exception. To summarize:
Make it an unchecked
Client code cannot do anything
exception
Client code will take some useful recovery action based on Make it a checked
information in exception exception
2. Preserve encapsulation.
This catch block just suppresses the exception and does nothing. The
justification is that there is nothing my client could do about an SQLException.
How about dealing with it in the following manner?
If you are confident that the business layer can take some recovery action
when SQLException occurs, you can convert it into a more meaningful checked
exception. But I have found that just throwing RuntimeException suffices most
of the time.
It is not giving any useful information to the client code, other than an
indicative exception name. Do not forget that Java Exception classes are like
other classes, wherein you can add methods that you think the client code
will invoke to get more information.
Even better, if you think the client code is not going to take any action other
than logging if the username is already taken, throw a unchecked exception:
Alternatively, you can even provide a method that checks if the username is
already taken.
4. Document exceptions.
You can use Javadoc's @throws tag to document both checked and unchecked
exceptions that your API throws. However, I prefer to write unit tests to
document exceptions. Tests allow me to see the exceptions in action and
hence serve as documentation that can be executed. Whatever you do, have
some way by which the client code can learn of the exceptions that your API
throws. Here is a sample unit test that tests for IndexOutOfBoundsException:
The next set of best practices show how the client code should deal with an
API that throws checked exceptions.
class DBUtil{
public static void closeConnection
(Connection conn){
try{
conn.close();
} catch(SQLException ex){
logger.error("Cannot close connection");
throw new RuntimeException(ex);
}
}
}
DBUtilis a utility class that closes the Connection. The important point is the
use of finally block, which executes whether or not an exception is caught. In
this example, the finally closes the connection and throws a RuntimeException if
there is problem with closing the connection.
2. Never use exceptions for flow control
try{
..
}catch(Exception ex){
}
Logging the same exception stack trace more than once can confuse the
programmer examining the stack trace about the original source of
exception. So just log it once.