Sections in this post Type of exceptions User defined custom exceptions Best practices you must consider and follow Never swallow the exception in catch block Declare the specific checked exceptions that your method can throw Do not catch the Exception class rather catch specific sub classes Never catch Throwable class Always correctly wrap the exceptions in custom exceptions so that stack trace is not lost Either log the exception or throw it but never do the both Never throw any exception from finally block Always catch only those exceptions that you can actually handle Don't use printStackTrace() statement or similar methods Use finally blocks instead of catch blocks if you are not going to handle exception Remember "Throw early catch late" principle Always clean up after handling the exception Throw only relevant exception from a method Never use exceptions for flow control in your program Validate user input to catch adverse conditions very early in request processing Always include all information about an exception in single log message Pass all relevant information to exceptions to make them informative as much as possible Always terminate the thread which it is interrupted Use template methods for repeated try-catch Document all exceptions in your application in javadocBefore we dive into deep concepts of exception handling best practices, lets start with one of the most important concepts which is to understand that there are three general types of throwable classes in Java: checked exceptions, unchecked exceptions, and errors.
Type of exceptions
Unchecked exceptions are exceptions that do not need to be declared in a throws clause. JVM simply doesn’t force you to handle them as they are mostly generated at runtime due to programmatic errors. They extend RuntimeException. The most common example is a NullPointerException [Quite scary.. Isn't it?]. An unchecked exception probably shouldn’t be retried, and the correct action should be usually to do nothing, and let it come out of your method and through the execution stack. At a high level of execution, this type of exceptions should be logged.
Errors are serious runtime environment problems that are almost certainly not recoverable. Some examples are OutOfMemoryError, LinkageError, and StackOverflowError. They generally crash you program or part of program. Only a good logging practice will help you in determining the exact causes of errors.
User defined custom exceptions
Anytime when user feels that he wants to use its own application specific exception for some reasons, he can create a new class extending appropriate super class (mostly its Exception.java) and start using it in appropriate places. These user defined exceptions can be used in two ways:1) Either directly throw the custom exception when something goes wrong in application
throw new DaoObjectNotFoundException("Couldn't find dao with id " + id);2) Or wrap the original exception inside custom exception and throw it
catch (NoSuchMethodException e) { throw new DaoObjectNotFoundException("Couldn't find dao with id " + id, e); }Wrapping an exception can provide extra information to the user by adding your own message/ context information, while still preserving the stack trace and message of the original exception. It also allows you to hide the implementation details of your code, which is the most important reason to wrap exceptions.
Now lets start exploring the best practices followed for exception handling industry wise.
Best practices you must consider and follow
1) Never swallow the exception in catch block1.
catch
(NoSuchMethodException e) {
2.
return
null
;
3.
}
2) Declare the specific checked exceptions that your method can throw
1.
public
void
foo()
throws
Exception {
//Incorrect way
2.
}
1.
public
void
foo()
throws
SpecificException1, SpecificException2 {
//Correct way
2.
}
1.
try
{
2.
someMethod();
3.
}
catch
(Exception e) {
4.
LOGGER.error(
"method has failed"
, e);
5.
}
4) Never catch Throwable class
Well, its one step more serious trouble. Because java errors are also subclasses of the Throwable. Errors are irreversible conditions that can not be handled by JVM itself. And for some JVM implementations, JVM might not actually even invoke your catch clause on an Error.
5) Always correctly wrap the exceptions in custom exceptions so that stack trace is not lost
1.
catch
(NoSuchMethodException e) {
2.
throw
new
MyServiceException(
"Some information: "
+ e.getMessage());
//Incorrect way
3.
}
1.
catch
(NoSuchMethodException e) {
2.
throw
new
MyServiceException(
"Some information: "
, e);
//Correct way
3.
}
1.
catch
(NoSuchMethodException e) {
2.
LOGGER.error(
"Some information"
, e);
3.
throw
e;
4.
}
7) Never throw any exception from finally block
1.
try
{
2.
someMethod();
//Throws exceptionOne
3.
}
finally
{
4.
cleanUp();
//If finally also threw any exception the exceptionOne will be lost forever
5.
}
8) Always catch only those exceptions that you can actually handle
1.
catch
(NoSuchMethodException e) {
2.
throw
e;
//Avoid this as it doesn't help anything
3.
}
9) Don’t use printStackTrace() statement or similar methods
Never leave printStackTrace() after finishing your code. Chances are one of your fellow colleague will get one of those stack traces eventually, and have exactly zero knowledge as to what to do with it because it will not have any contextual information appended to it.
10) Use finally blocks instead of catch blocks if you are not going to handle exception
1.
try
{
2.
someMethod();
//Method 2
3.
}
finally
{
4.
cleanUp();
//do cleanup here
5.
}
11) Remember “Throw early catch late” principle
This is probably the most famous principle about Exception handling. It basically says that you should throw an exception as soon as you can, and catch it late as much as possible. You should wait until you have all the information to handle it properly.
This principle implicitly says that you will be more likely to throw it in the low-level methods, where you will be checking if single values are null or not appropriate. And you will be making the exception climb the stack trace for quite several levels until you reach a sufficient level of abstraction to be able to handle the problem.
12) Always clean up after handling the exception
If you are using resources like database connections or network connections, make sure you clean them up. If the API you are invoking uses only unchecked exceptions, you should still clean up resources after use, with try – finally blocks. Inside try block access the resource and inside finally close the resource. Even if any exception occur in accessing the resource, then also resource will be closed gracefully.
13) Throw only relevant exception from a method
Relevancy is important to keep application clean. A method which tries to read a file; if throws NullPointerException then it will not give any relevant information to user. Instead it will be better if such exception is wrapped inside custom exception e.g. NoSuchFileFoundException then it will be more useful for users of that method.
14) Never use exceptions for flow control in your program
We have read it many times but sometimes we keep seeing code in our project where developer tries to use exceptions for application logic. Never do that. It makes code hard to read, understand and ugly.
15) Validate user input to catch adverse conditions very early in request processing
Always validate user input in very early stage, even before it reached to actual controller. It will help you to minimize the exception handling code in your core application logic. It also helps you in making application consistent if there is some error in user input.
For example: If in user registration application, you are following below logic:
1) Validate User
2) Insert User
3) Validate address
4) Insert address
5) If problem the Rollback everything
This is very incorrect approach. It can leave you database in inconsistent state in various scenarios. Rather validate everything in first place and then take the user data in dao layer and make DB updates. Correct approach is:
1) Validate User
2) Validate address
3) Insert User
4) Insert address
5) If problem the Rollback everything
16) Always include all information about an exception in single log message
LOGGER.debug(“Using cache sector A”);
LOGGER.debug(“Using retry sector B”);
Don’t do this.
Using a multi-line log message with multiple calls to LOGGER.debug() may look fine in your test case, but when it shows up in the log file of an app server with 400 threads running in parallel, all dumping information to the same log file, your two log messages may end up spaced out 1000 lines apart in the log file, even though they occur on subsequent lines in your code.
Do it like this:
LOGGER.debug(“Using cache sector A, using retry sector B”);
17) Pass all relevant information to exceptions to make them informative as much as possible
This is also very important to make exception messages and stack traces useful and informative. What is the use of a log, if you are not able to determine anything out of it. These type of logs just exist in your code for decoration purpose.
18) Always terminate the thread which it is interrupted
1.
while
(
true
) {
2.
try
{
3.
Thread.sleep(
100000
);
4.
}
catch
(InterruptedException e) {}
//Don't do this
5.
doSomethingCool();
6.
}
1.
while
(
true
) {
2.
try
{
3.
Thread.sleep(
100000
);
4.
}
catch
(InterruptedException e) {
5.
break
;
6.
}
7.
}
8.
doSomethingCool();
There is no use of having a similar catch block in 100 places in your code. It increases code duplicity which does not help anything. Use template methods for such cases.
For example below code tries to close a database connection.
01.
class
DBUtil{
02.
public
static
void
closeConnection(Connection conn){
03.
try
{
04.
conn.close();
05.
}
catch
(SQLException ex){
06.
throw
new
RuntimeException(
"Cannot close connection"
, ex);
07.
}
08.
}
09.
}
01.
public
void
dataAccessCode() {
02.
Connection conn =
null
;
03.
try
{
04.
conn = getConnection();
05.
....
06.
}
finally
{
07.
DBUtil.closeConnection(conn);
08.
}
09.
}
Make it a practice to javadoc all exceptions which a piece of code may throw at runtime. Also try to include possible course of action, user should follow in case these exception occur.
That’s all i have in my mind for now. If you found anything missing or you does not relate to my view on any point, drop me a comment. I will be happy to discuss.
(Source: Internet)
No comments:
Post a Comment