In my previous tutorial about Java exceptions, I have covered suppressed exceptions and java exception handling. It is one of the important coding practice to do efficient and foolproof exception handling for your projects. To achieve that, you must have a good knowledge on best practices used for exception. This tutorial will highlight the most significant best practices used by the Java programmers to improve the quality of exception handling. If you have any questions, please write it in the comments section.
1. Do not suppress / swallow the exception in catch block
public class ExceptionExample { public FileInputStream testMethod1(){ File file = new File("test.txt"); FileInputStream fileInputStream = null; try{ fileInputStream = new FileInputStream(file); fileInputStream.read(); }catch (IOException e){ return null; } return fileInputStream; } public static void main(String[] args){ ExceptionExample instance1 = new ExceptionExample(); instance1.testMethod1(); } }
Swallowing the exception is one of the bad practice in the exception handling. In the above example, whatever exception thrown will be ignored completely and there is no clue on where is the problem. It is always best practice to throw the exception back to outer layer or handle the exception and perform some alternative solution.
public class ExceptionExample { public FileInputStream testMethod1() throws IOException{ File file = new File("test.txt"); FileInputStream fileInputStream = null; try{ fileInputStream = new FileInputStream(file); fileInputStream.read(); }catch (IOException e){ throw e; } return fileInputStream; } public static void main(String[] args) throws IOException{ ExceptionExample instance1 = new ExceptionExample(); instance1.testMethod1(); } }
2. Declare specific exception in throws clause of method block
Writing like the below throws clause defeat the whole purpose of the checked exceptions:
public FileInputStream testMethod1() throws Exception{
This is not good practice. We must always try to use the most specific exception class that are thrown from the program. The most appropriate throws
clause would be:
public FileInputStream testMethod1() throws IOException{
3. Catch specific exception classes
It is always good idea to catch the specific classes that are thrown from the application. If there are multiple exceptions thrown from the application that are falls under the same type, catching only the super class is not an good idea.
For instance, if your code is throwing both FileNotFoundException
and IOException
, have two catch clauses for both the exceptions on hierarchy instead of writing one catch clause with IOException
or in worst cases just writing catch clause for Exception
.
Here is the correct way to catch the specific exception class:
try { //some statements catch(FileNotFoundException e){ //handle here } catch(IOException e){ //handle here }
You should never catch the exception like this:
try { //some statements catch(Exception e){ //handle here }
4. Release the resources in Finally block
When you are opening a database connection, file operations or any other resources centric operations would be closed properly, otherwise that will lead to a performance issue for your application. For instance, when you have opened a database connection and performing CRUD operations, any exceptions thrown will leave the connection open for ever and it will never be closed.
To avoid the worst scenario, it is always good practice to close the opened resources in the finally block. Here is example code for closing the database connections in the finally block:
finally { try { if (con != null) { con.close(); } if (stat != null) { stat.close(); } } catch (SQLException sqlee) { sqlee.printStackTrace(); } }
Next time when you see your team member not using the finally block for closing the resources, pinch him to follow the best practice :).
5. Exceptions Impacts Performance
Exceptions are very very expensive. This statement should be remembered by every Java programmer while they are writing the code. Creating an exception is very slow operation and throwing an exception cost you around 1-5 microseconds. When the exceptions are propagated to multiple levels, then it will slow down the entire application.
- Use the exceptions in the exceptional conditions
- Use the exceptions when it is recoverable
While exception usage is good you should better avoid capturing too many stack traces in your program. In many cases they are not even necessary to understand the problem – especially if they cover a problem you already expect. The exception message therefore might prove as being enough information.
6. Use Standard Exceptions
Don’t write your custom exceptions if the problem can be dealt with the in-built exceptions. Java API itself provides hundreds of exceptions for various conditions. First try to use the same exceptions for your application, if that is not sufficient and not providing meaning of your business scenario, then create your own custom exceptions.
If you use the in-built exceptions, then any new developer or outside world also will be able to understand the code. You need not lock your code with your own API.
7. Wrap the exceptions correctly
When you re-throw your applications, it is important to wrap the original exceptions properly, otherwise the origin of the exceptions will be lost. Look at the example:
import java.io.IOException; public class HelloWorld{ public static void main(String []args) throws Exception{ try{ throw new IOException("IOException"); }catch (IOException e){ throw new ExampleException1("Example Exception and " + e.getMessage()); } } } class ExampleException1 extends Exception{ public ExampleException1(String s, Throwable t){ super(s,t); } public ExampleException1(String s){ super(s); } }
Output for the above program will be as below:
Exception in thread "main" ExampleException1: Example Exception and IOException at HelloWorld.main(HelloWorld.java:8)
In the above output, the IOException stack trace is lost because we have not used the correct constructor to wrap the exceptions. Modify the catch block as below, this the proper way of wrapping the exceptions:
catch (IOException e){ throw new ExampleException1("Example Exception",e); }
The output will be:
Exception in thread "main" ExampleException1: Example Exception at HelloWorld.main(HelloWorld.java:8) Caused by: java.io.IOException: IOException at HelloWorld.main(HelloWorld.java:6)
8. Avoid throwing exception from finally block
try { method(); //here throws first exception } finally { shutdown(); //If finally blockthrew any exception the first exception will be lost forever }
In the above example snippet, note that there is a possibility of finally block also would thrown an exception. If it throws an exception, then first exception will be lost in case both the exception thrown at the same time. It is good practice to handle the exception in finally block or log the error messages. But, never thrown any exception from the finally block.
9. Don’t use exceptions for flow control
You should never use exception for controlling the flow of your program. For example, instead of using the if condition, using exception to validate a scenario is one of the worst practice that impacts the performance if your application.
10. Don’t catch Throwable class
You should never catch Throwable class in your program. Error also subclass of Throwable class, the errors are not even recoverable by the JVM itself.
11. Document the exceptions
Make it a habit to document all the exceptions in your application. When you write a custom exception in your application, no one else in the world other than you know the cause for that exception :). So, you must do a favor to your team by writing a good explanation about the cause of that exception and how to handle it. Trust me, that will be a great help when needed.