Top Exception handling Best Practices in Java

Exception handling in a robust application is very important part. It is not part of functional requirement of any product but it is necessary to handle any ill condition like connection not available, resource not found, invalid input, null input and so on.
In this best practices tutorial , I will discuss some little known practices which must be considered while handling exceptions in a robust application. Java provides many ways to handle exceptions like using try, catch and finally keyword. For more information about java exception handling follow this link. In Java we can also create new exceptions and throw them using throw and throws keyword.

In this java exception handling best practices tutorial I have mention near about top 10 to 20 java best practices to write exception handling code. Before going to read this tutorial learn when to use checked vs unchecked exceptions in Java.

Type of Exceptions in Java

  1. Checked Exceptions
  2. Unchecked Exceptions.

Checked Exceptions

These are the type of exceptions for which the compiler checks to ensure that your code is prepared for handling such exceptions. These are the exceptions that you can expect to occur frequently and you must handle them in your code. The conditions that generate such exceptions are generally outside the control of your program and they can occur in a correct program. But you can anticipate them and thus you must write code to deal with them. Programmatically, checked exceptions are the instances of the Exception class or one of its subclasses, excluding RuntimeException subtree.

Unchecked Exceptions

The compiler does not check for such type of exceptions. Unchecked Exceptions comprise of run time exceptions (of type RuntimeException or its subclasses) and errors (of type Error or its subclasses). Runtime Exceptions occur due to program bugs and include exceptions such as division by zero and invalid array indexing.

Top Exception handling Best Practices in Java

 


Java Exception Handling Best Practices

Use Checked Exception for Resolvable error and Unchecked Exception for run-time error.
Use Checked Exception on these conditions that generate such exceptions are generally outside the control of your program and they can occur in a correct program. Unchecked Exceptions comprise of run time exceptions (of type RuntimeException or its subclasses) and errors (of type Error or its subclasses). Runtime Exceptions occur due to program bugs and include exceptions such as division by zero and invalid array indexing. You can not recover from an unchecked exception and you are not required to handle such type of exceptions either, but still you can do so if you want.

Declare the specific checked exceptions that your method can throw
Always declare the specific checked exceptions that your method can throw. Suppose there are many such checked exceptions, you should probably wrap them in your own user defined exception and add information to in exception message. You can also use comma between multiple exceptions throws.

public void method() throws Exception { //Incorrect way
}

public void method() throws SpecificException1, SpecificException2 { //Correct way
}

Never throw any exception from finally block
Finally block used for clean up operation and never throw any exception in this block. If clean up operation throw any exception then second exception will come out of method and the original first exception i.e. correct reason will be lost forever. In such condition you can also handle exception in the finally block.

try {
  method();  //Throws exception One
} finally {
  cleanUp();    //If finally also threw any exception the exception One will be lost forever
}

Close or release resource in finally block
Always release all resources after successfully completion or any occurring any exception between the process. So functionality like release resource, close connection of DB or Network. This is a well known best practice in Java. Closing resources in finally block guarantees that precious and scarce resource released properly in case of normal and aborted execution, guaranteed by finally block.

Use finally blocks instead of catch blocks if you are not going to handle exception
If you don’t want to handle exception then always finally block with try instead of using try-catch. This is also a good practice.

try {
  doSomething(); 
} finally {
  cleanUp();    //do cleanup here
}

If above code you are accessing some method doSomething() but this method may throw some exception which you do not want to handle in own calling method, but still want some cleanup in case exception occur, then do this cleanup in finally block.

Always include all information about an exception in single log message
Don’t do this:

LOGGER.debug("Using cache sector A");
LOGGER.debug("Using retry sector B");

Do it like this:

LOGGER.debug("Using cache sector A, using retry sector B");

Throw only relevant exception from a method
Always throw relevant exception from a method because it is important to keep application clean. Suppose a method which tries to make connection to DB if you throws NullPointerException then it will not give any relevant information to user. So either you could predefined relevant exception or you could make own custom exception.

Always provide meaning full message on Exception
Always provide meaning full message of Exception to the logs because meaning full message helps to developer to find problem in the application. Always try to provide precise and factual information here. For example, compare these two Exception messages for IllegalArgumentException :

message 1: “Incorrect argument for method”
message 2: “Illegal value for ${argument}: ${value}

first one just says that argument is illegal or incorrect, but second one include both name of argument and its illegal value which is important to point out cause of error.

Avoid empty catch blocks
Never use empty catch block because it not just hides the Errors and Exception, but also may leave your object in unusable or corrupt state.

Never swallow the exception in catch block
This point is also same as leaving empty catch block when handling the exception. Return null from catch block instead of handling the exception, it is cause of losing the error cause forever.

catch (NoSuchMethodException e) {
   return null;
}

Use template methods for repeated try-catch
For repeated catch block of same code in the many places in the application increase the duplicity of code, so always make a template method of this code and use this template method.

class DBUtil{
    public static void closeConnection(Connection conn){
        try{
            conn.close();
        } catch(Exception ex){
            //Log Exception - Cannot close connection
        }
    }
}

public void dataAccessCode() {
    Connection conn = null;
    try{
        conn = getConnection();
        ....
    } finally{
        DBUtil.closeConnection(conn);
    }
}

Never use exceptions for flow control in your program
Exceptions are only erroneous condition in the application code but sometime developer use it as a flow control. Never do that.

try{
    conn.close();
} catch(Exception ex){
    //Writing application logic here
}

Always clean up after handling the exception
Resources like DB connection, Network connections etc. should cleaned up after code execution either successfully completed or throwing any exceptions. If the API you are invoking uses only unchecked exceptions, you should still clean up resources after use, with try – finally blocks.

Always catch only those exceptions that you can actually handle

catch (NoSuchMethodException e) {
   throw e; //Avoid this as it doesn't help anything
}

If you can handle an exception then catch it otherwise re-throw it.

Converting Checked Exception into RuntimeException
This is one of best practices of Exception Handling, many of frameworks like Spring adopt it for handling exception. According to it framework converts checked exception into RuntineException. For example in Spring JDBC framework has converted exception like SQLException wrapped into DataAccessException, an unchecked Exception. This Java best practice provides benefits, in terms of restricting specific exception into specific modules, like SQLException into DAO layer and throwing meaningful RuntimeException to client layer.

Including cause of Exception in stack-trace
Java Exception class provides getCause() method to retrieve cause which can be used to provide more information about root cause of Exception. This Java best practice helps a lot while debugging or troubleshooting an issue.

Document Exception thrown by any method
Should always make document for all exceptions which a piece of code may throw at runtime. This becomes increasingly important if you are writing API or public interface. With proper documentation of Exception thrown by any method you can potentially alert anyone who is using it.

 

Previous