JavaData & JavaUnderstanding Exception Handling in Java

Understanding Exception Handling in Java

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Most modern programming languages support an exception handling mechanism. It is an important and essential feature of the language. Java is a bit strict and imposes the rule of explicit exception handling to almost every susceptible situation, whereas C++ is a bit more lenient and features it as a good programming practice. Java’s eye of cautiousness results in a rigor of additional coding, no doubt, but at the end it can be quite relieving, especially during the testing and debugging phases. Java Exception is particularly useful in creating a robust and fault-tolerating system. The APIs provide extensive support to deal with the problems yet to occur and terminate gracefully. Terminate gracefully—is exactly what exception handling is meant to do. The article is an attempt to understand the key concepts of exception handling in Java.

Java Exception

Java exception is an object that defines an problem or unusual situation that occurred during program execution. It is generally thrown and caught with the help of a try…catch block. These blocks contain measures/instructions that must be executed to gain control of the situation. By the way, exceptions are not errors per se, though they may sound a bit similar. An error represents a situation from where recovery is not possible. It generally indicates a serious problem that most applications should not even try to catch. Exceptions, on the other hand, are manageable situations, such as an attempt to divide a number by zero. But, if an application runs out of memory, it is an error and not an exception.

Exceptions basically represent an indication of a problem or an unusual situation during program execution that require some special attention. It is possible to take care of these situations before their occurrence. For example:

Suppose we write a code to load a class definition by its string name at runtime as:

Class.forName("MyClass");

During execution, it was found that the class definition specified by the name could not be found. In such a situation, we let two things happen:

  • Let the application crash or terminate abruptly by not handling the situation.
  • See to it that the program in any case terminates gracefully, stating the reason of its failure.

As should be obvious, the second option is the natural choice that we like to implement. In that case, what we need to do is enclose the code within an appropriate exception block and handle any such situation.

try{
   // ...
   Class.forName("MyClass");
   // ...
}catch(ClassNotFoundException ex){
   // ...
   System.out.println("MyClass not found. Make sure that
      the class exists"
      + " or is in the right path, then try again);
}

Java Exception saves a lot of debugging and testing time and also minimizes the chance of application crashes due to unforeseen circumstances by the programmer. Handling exception is like laying out certain norms to follow even though a situation may have gotten out of control.

Java Exception Classification

The class organization of Java Exception is pretty simple. Let’s understand it intuitively; we generally face two types of problems, such as errors or exceptions. We either can deal with them and come to a point of compromise or completely disregard them and throw them out, not caring about the consequence. As a result, to keep up both the possibilities, all error and exception classes are subordinated by the Throwable class. That means the classes that are designated (subclasses of the Throwable class) by Throwable are eligible only to be thrown by the Java Virtual Machine (JVM) or we can do it explicitly by using the throw statement.

The problems that are thrown to be caught (handled) are actually caught by the catch clause. The catch clause states that only the subclass of the Throwable class can be a candidate of its argument type. As a result, the Throwable class is the super class of all errors and exceptions together.

Because we are interested in the branch of exception, observe in Figure 1 that all the exception classes are subclass of Exception. When coding, if we are unsure of the type of exception to handle, we can always use the Exception reference type in the catch clause.

Except
Figure 1: The exception branch

Note: Refer to the Java API Documentation for a more detailed list on classification.

 

Code Snippet Description
try{
   // ...
}catch(Exception ex){
   // ...
}
The Exception object reference can refer to any exception because Exception is the super class of all exceptions in Java. This is particularly suitable in a situation where we are not sure exactly what exception may occur during execution.
try{
   // ...
}catch(Exception ex){
   // ...
}finally{
   // ...
}
The finally block is optional, and can be included as per need. For example, if we have opened files or a database that we want to close gracefully, we may write those close statements in the finally block.
try{
   // ...
}catch(ArithmeticException ex){
   // ...
}
Only handles ArithmeticException. Because ArithmeticException is a leaf exception class, the other exception remains unhandled. This is used when we are sure that a specific exception may occur.
try{
   // ...
}catch(SqlException | ClassNotFoundException ex){
   // ...
}
We can use a logical OR operator to handle multiple exception scenarios and deal with them. This is useful when we know exactly what exceptions may occur and deal with them collectively.
try{
   // ...
}catch(SqlException ex1){
   // ...
}catch(ClassNotFoundException ex2){
   // ...
}
If we want to deal with each exceptions separately, we may use one or more catch clause.
try{
   // ...
   try{
      // ...
   }catch(SqlException ex1){
      // ...
   }
}catch(SqlException ex2){
   // ...
}finally{
   try{
      // ...
   }catch(SqlException ex1){
      // ...
   }
}
Exceptions can be nested and they can be nested anywhere to virtually any depth. A word of caution: Too many nesting is not a good idea, although it may be syntactically right.

In a way, the try block is the playground where every good and bad things happen. The catch block is the mitigating care taker that catches the bad situation and deals with it as defined. And, finally does things that must be done under any circumstances.

Typically, we keep our major code in the try block. In catch, we handle the exception and write code in the optional finally block that must execute in any case.

A Simple Experiment of a Graceful Exit

In Java class, design it is expected that the programmer takes care of the exception so that the application does not terminate abnormally even if an exception occurs during execution. The program should be able to produce a message describing the situation due to which an exception occurred and where it was produced. This information is quite vital in determining the problem that immediately can be tracked down and rectified.

Let’s implement a simple scenario to illustrate the difference between explicitly handing an exception and not handling one at all.

In the first scenario, observe the output of the class in Listing 1. The program terminates abnormally, throwing an ArithmeticException when an invalid arithmetic operation (divide by zero, in this case) is attempted. Also, the println line after result=divResult(10,0) in main did not execute. This gives the clue that the program has terminated abnormally. Because there is no code to handle the exception explicitly, the program terminates by printing specific information of the situation. Now, imagine a situation where there are thousands of line of code. Such a minimal message does not provide enough information to pinpoint exactly where the exception occurred.

package org.mano.example;

public class ExceptionDemo1 {

   public static void main(String[] args) {
      int result = divResult(10, 3);
      System.out.println("Result = " + result);
      result = divResult(10, 0);
      System.out.println("Result = " + result);
   }

   public static int divResult(int divisor, int dividend) {
      int result = 0;
      result = divisor / dividend;
      return result;
   }
}

Listing 1: No exception handling

Output

Result = 3
Exception in thread "main" java.lang.ArithmeticException: / by zero
   at org.mano.example.ExceptionDemo1.divResult(ExceptionDemo1.java:15)
   at org.mano.example.ExceptionDemo1.main(ExceptionDemo1.java:8)

We, however, can customize the exception message to serve our purpose. Observe the output of Listing 2. Here we have explicitly handled the situation by encompassing the critical code within the try…catch block. The situation is handled nicely, providing a suitable message of the problem so that the real cause can be figured out easily. Above all, the program is terminated gracefully. The println() line after result=divResult(10,0) in main now executes normally. This refers to a graceful handling of the situation and program terminated normally as expected.

package org.mano.example;

public class ExceptionDemo1 {

   public static void main(String[] args) {
      int result = divResult(10, 3);
      System.out.println("Result = " + result);
      result = divResult(10, 0);
      System.out.println("Result = " + result);
   }

   public static int divResult(int divisor, int dividend) {
      int result = 0;
      try {
         result = divisor / dividend;
      } catch (ArithmeticException arithmeticException) {
         System.err.println("Exception occured in static method:
            divResult(int divisor, int dividend)");
         System.err.printf("Divisor=%d, Dividend=%d.
            Dividend cannot be zeron", divisor, dividend);

      } finally {

      }
      return result;
   }
}

Listing 2: Explicit exception handling

Output

Result = 3
Exception occurred in static method:
   divResult(int divisor, int dividend)
Divisor=10, Dividend=0. Dividend cannot be zero
Result = 0

Conclusion

Abrupt termination of an application is not only irritating but also a nuisance. The user has every right to at least know what went wrong. An exception handling mechanism provides the way to not only deal with exceptions but also show the exact point of their occurrences with appropriate messages. The program then, at the peak of execution, can step down (exit) gracefully rather then jump from the cliff (terminate abruptly).

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories