How to use exceptions effectively

Exception is an event which occurs during the execution of a program and disrupts its normal flow, it is mainly arrised from different kind of situations such as wrong user interaction, hardware failure, network failure, database failures or even business failures.

1. Checked vs Unchecked

Exceptions are of 2 types: checked and unchecked.

Exception Class Hierarchy

Checked exceptions are predictable during the development phase, they are the result of invalid business scenarios or wrong user interaction, this type of exceptions is recoverable and the developer is forced to recover from them before publishing an application, some common checked exceptions are:  FileNotFoundException, IOException

In contrast, unchecked exceptions can’t be predicted during the development phase, they occur at runtime either due to programming errors like: NullPointerException or due to a harmful situation which causes the current thread to halt and kills the application like: OutOfMemoryException. Although developers are able to handle these exceptions programatically, it is not recommended to recover from them since they normally signal a serious issue which requires investigation and technical resolution.

2. Exception handling techniques

In this section, we list the best practices to be followed when handling exceptions in OOP environments:

  1. Use exceptions appropriately: exceptions are costly, when overused they can slow down your application. The common case for throwing manually an exception in your code is to force the caller to recover from ubnormal scenario, if you feel that response codes or boolean variables fit your needs, then prefer them over exceptions. In addition, exceptions shouldn’t be used for controlling the flow of your program, if/else and switch blocks already satisfy this purpose without additional costs.

    One of the common misuse of exceptions is to iterate over an array without a stop condition and depend on the exception to finish the iteration, the above should be simply written as:
  2. Use specific exceptions: All exceptions inherit from Exception class, when throwing an exception in your program, it is recommended to throw a specific and non-general exception  so that your caller knows the root cause and recover easily from it, it is better to throw multiple specific exceptions than throwing a general exception. The same logic should apply for catching exceptions, regardless how many catch blocks you define, never catch Exception class. Following is an example for reading a file from file system, instead of catching exceptions like this:

    write several specific exceptions in order to do custom recovery logic for each error:
  3. Never ignore an exception: exceptions should be logged, you will never be able to debug any issue in your application if you ignore an exception , the program might fail at an arbitrary time in the future at a point in the code that bears no apparent relation to the source of the problem, at this time, debugging is the hardest task ever. Simply don’t use empty catch blocks.
  4. Handle or propagate: an exception should be handled only by the responsible class who knows how to recover from it, when a class catches an exception it is better to propagate it up the stack if it has nothing to do with it.
  5. Use abstract exceptions: it is a common technique to implement custom exceptions related to each component in your application so that you decouple the components from each other, therefore whenever you change the implementation of some component and replace some exceptions, other components would not break. For example, suppose you write a data access component which communicates with oracle database, several operations would throw SQLException upon failure, if you decided to throw the exception as is to other components, then you are coupling other components with the low level implementation of your data access layer, if you ever decided to use XML instead of oracle as a data store, your code will break. The best practice is to throw a custom exception like DataAccessException holding an error code and a specific error message.
  6. Never display exceptions to end user: when an exception occurs in an application which responds to end users, the exception should be handled in the application and converted to a user friendly message, the stack trace should never be displayed to the user.
  7. Use common exceptions and errors codes in API: when implementing an API which interacts with external applications, your API should be designed to throw common exceptions which could be understood easily by developers, for example, use IllegalArgumentException when developers pass illegal arguments to your API, IllegalStateException when developers pass a non-initialized object, and ConcurrentModificationException when your API is being accessed from multiple threads, this way the developers who are using your API can understand what happens exactly and react accordingly instead of putting time on understanding your custom exception. However there are some common situations where you want to inform your clients of some business failures, in this case, it is preferred to use error objects with error codes and messages instead of exceptions.
  8. Add usable information to your exception: When exception is thrown, it should hold usable and detailed information which helps the handler to recover from it, or the developer to fix it. Common information would be: the exact location of the error in the code + line number, the business scenario which caused the error in addition to the values of the parameters that contributed to the exception.
  9. Closing resources: it is a common practice that each class who catches an exception should close the opened resources that it knows about in the finally block regardless if it handles or propagates it, since resources can halt the system if remain opened.
  10. Exceptions shouldn’t break object’s state: Generally speaking, a failed method invocation should leave
    the object in the state that it was in prior to the invocation. The common way to achieving failure atomicity is to order the computation so that any part that may fail takes place before any part that modifies the object.
  11. Either survive or shut down: When an exception occurs, the application either survives and continues to process requests OR shuts down gracefully, in both ways the error should be logged and the required parties should be notified. If the error causing the exception is so severe that the application cannot survive e.g. required configuration file is missing, the application must shut down gracefully.

That’s the common techniques that i personally use when handling exceptions, i hope you liked it and learned something from it. Should you have any other techniques, please share them with me in the comments section below.

 

Hussein Terek

Founder of programmergate.com, I have a passion in software engineering and everything related to java environment.

You may also like...

Leave a Reply

avatar