Google

Nov 18, 2014

Java exception handling beginner mistakes and best practices

a) Sweeping exceptions under the carpet by doing nothing.

try{
     //...
}catch(SQLException sqe){
    // do nothing
}

In few rare scenarios, it is desired to do nothing with an exception, e.g. in a finally block, you try to close database connection, and some exception occurs. In this case, exception can be ignored.

try{
 
}catch(SQLException sqe){
    //...
}finally{
    try{
        conn.close();
    }catch(Exception e){
        //leave it.
    }
}

But in general, this is a very bad practice and can hide issues. Another typical example is  the InterruptedException. Most Java developers leave the catch block empty. This is a very bad practice. You should either re throw e or restore the interrupted status as shown below

  public void run() { 
    try {
        while (true) {
           //....
    Thread.sleep(1000);
        }
    }
    catch (InterruptedException e) { 
       // Restore the interrupted status
       Thread.currentThread().interrupt();
    }
  }


If a thread is executing a low-level interruptible blocking method like Thread.sleep( ), Thread.join( ), or Object.wait( ), it unblocks and throws InterruptedException. The interrupted status can be read with Thread.isInterrupted( ).

b) Inconsistent use of checked and unchecked (i.e. Run time) exceptions.

Document a consistent exception handling strategy. In general favor unchecked (i.e. Run time) exceptions, which you don't have to handle with catch and throw clauses. Use checked exceptions in rare scenarios where you can recover from the exception like deadlock or service retries. In this scenario, you will catch a checked exception like java.io.IOException and wait a few seconds as configured with the retry interval, and retry the service configured by the retry counts. After a few retries, the exception is thrown to the caller.




c) Exceptions are polymorphic in nature and more specific exceptions need to be caught before the generic exceptions.

So, it is wrong to catch Exception before IOException.

try{
   //....
} catch(Exception ex){
    log.error("Error:" + ex)
} catch(IOException ex){
    log.error("Connectivity issue:" + ex); //never reached as Exception catch block                                           //catches everything
}


Fix this by catching the more specific IOException first

try{
   //....
} catch(IOException ex){
    log.error("Connectivity issue:" + ex);
} catch(Exception ex){
    log.error("Error:" + ex)
} 


In Java 7 onwards, you can catch multiple exceptions like

try{
   //....
} 
catch (ParseException | IOException exception) {
    // handle I/O problems.
} catch (Exception ex) {
    //handle all other exceptions
}


d) Wiping out the stack trace

try{
    //....
}catch(IOException ioe){
    throw new MyException("Problem in data reading."); //ioe stack is lost
}


e) Unnecessary Exception Transformation.

In a layered application, many times each layer catches exception and throws new type of exception. Sometimes it is absolutely unnecessary to transform an exception. An unchecked excption can be automatically bubbled all the way upto the GUI Layer, and then handled at the GUI layer with a log.error(ex) for the log file and a generic info like "An expected error has ocurred, and please contact support on xxxxx" to the user. Internal details like stack trace with hostnames, database table names, etc should not be shown to the user as it can be exploited to cause security threats.

Data Access Layer --> Business Service Layer --> GUI Layer


f)  why throw exceptions early, and catch exceptions late?

Best practice is to throw exceptions at the point when the errors occur so that you have the most detail about the cause of the exception. For example, you want to know the line that throws the NullPointerException so that you can figure out which variable is null. NullPointerException means you have some broken code, and you need to fix your code. A good example for throwing early would be to throw IllegalArgumentException.

  public void someMethod(String input){
      if(StringUtils.isEmpty(input)){
      throw new IllegalArgumentException("input cannot be empty");
      }
   
      //.....do something
  }

Catching an exception too early before it can properly be handled, often leads to further errors and exceptions.

Bad: Catching it too early

public void readFile(String filename) 
{
    //...
    
    InputStream in = null;
    
    // Don't do this !!! 
    try
    {
        in = new FileInputStream(filename);
    }
    catch (FileNotFoundException e)
    {
        logger.log(e);
    }
    
     in.read(...);
    
    //...
}

Good: Catching exceptions late

public void readFile(String filename) throws IOException
{
    if (filename == null)
    {
        throw new IllegalArgumentException ("filename is null");
    }  
    
    //...
    
    InputStream in = new FileInputStream(filename);
    
    //...
}


In many situations, the higher layer like the GUI layer knows how to handle the exceptions than the lower layers like data access objects. In many cases, same data access exception will be handled differently by the different calling code. If an exception is thrown, you catch it and decide on the following 4 possible actions to take.
  1. Catch --> Rethrow (but include the original exception with some additional information). 
  2. Catch --> Handle (mainly in the higher layer (e.g. GUI layer) where you make the final decision like logging the exception and asking the user to contact support).
  3. Let the exception bubble up to the higher layers
  4. Catch --&gt Return an error code (for example in batch jobs, Unix scripts, etc). Usually in a higher layer.



Labels: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home