Java Exception Handling Tutorial

1- What is Exception?

First, let's see the following illustration example:
In this example, there is an part of error code which results from the division by 0. The division by 0 causes the exception:  ArithmeticException
HelloException.java
package org.o7planning.tutorial.exception;

public class HelloException {

   public static void main(String[] args) {

       System.out.println("Three");

       // This division no problem.
       int value = 10 / 2;

       System.out.println("Two");

       // This division no problem.
       value = 10 / 1;

       System.out.println("One");
       
       // This division has problem, divided by 0.
       // An error has occurred here.
       value = 10 / 0;

       // And the following code will not be executed.
       System.out.println("Let's go!");

   }

}
The result from running the example:
You can see the notification on the Console screen. The error notification is very clear, including the information of code line.
Let see the flow of the program through the following illustration:
  • The program runs normally from step (1), (2) to (5).
  • In step (6), the program divided by 0.
  • The program jumps out of main method, and the (7) code line has not been executed.

We will modify code of above example.

HelloCatchException.java
package org.o7planning.tutorial.exception;

public class HelloCatchException {

   public static void main(String[] args) {

       System.out.println("Three");

       // This division no problem.
       int value = 10 / 2;

       System.out.println("Two");

       // This division no problem.
       value = 10 / 1;

       System.out.println("One");

       try {
           // This division has problem, divided by 0.
           // An error has occurred here.            
           value = 10 / 0;

           // And the following code will not be executed.
           System.out.println("Value =" + value);

       } catch (ArithmeticException e) {

           // The code in the catch block will be executed
           System.out.println("Error: " + e.getMessage());

           // The code in the catch block will be executed
           System.out.println("Ignore...");

       }

       // This code is executed
       System.out.println("Let's go!");

   }

}
And the results of running the example:
We will explain the flow of the program by the following illustration images.
  • Steps (1) to (5) are completely normal.
  • Exception occurs in step (6), the problem divided by zero.
  • Immediately it jumps in executing the command in catch block, step (7) is skipped.
  • Step (8), (9) is executed.
  • Step (10) is executed.

2- Exception Hierarchy

This is the model of hierarchical map of Exception in Java.
  • The highest class is Throwable
  • Two direct subclasses is Error and Exception.
In Exception branch there is a RuntimeException subclass including exceptions unchecked by Java in the compile-time. The meaning of check and uncheck in the compile-time is illustrated in examples of the next part. 

Note: Your class should inherited from two branches Error or Exception, not directly inherited from Throwable.

Errors

 When a dynamic linking failure or some other "hard" failure in the virtual machine occurs, the virtual machine throws an Error. Typical Java programs should not catch Errors. In addition, it's unlikely that typical Java programs will ever throw Errors either.

Exceptions

Most programs throw and catch objects that derive from the Exception class. Exceptions indicate that a problem occurred but that the problem is not a serious systemic problem. Most programs you write will throw and catch Exceptions.

The Exception class has many descendants defined in the Java packages. These descendants indicate various types of exceptions that can occur. For example, NegativeArraySizeException indicates that a program attempted to create an array with a negative size.

    One Exception subclass has special meaning in the Java language: RuntimeException.

Runtime Exceptions

The RuntimeException class represents exceptions that occur within the Java virtual machine (during runtime). An example of a runtime exception is NullPointerException, which occurs when a method tries to access a member of an object through a null reference. A NullPointerException can occur anywhere a program tries to dereference a reference to an object. The cost of checking for the exception often outweighs the benefit of catching it.

 Because runtime exceptions are so ubiquitous and attempting to catch or specify all of them all the time would be a fruitless exercise (and a fruitful source of unreadable and unmaintainable code), the compiler allows runtime exceptions to go uncaught and unspecified.

 The Java packages define several RuntimeException classes. You can catch these exceptions just like other exceptions. However, a method is not required to specify that it throws RuntimeExceptions. In addition, you can create your own RuntimeException subclasses. Runtime Exceptions--The Controversy contains a thorough discussion of when and how to use runtime exceptions.

3- Handling exception with try-catch

We write a class that inherits from Exception.
AgeException.java
package org.o7planning.tutorial.exception.basic;

public class AgeException extends Exception {

  public AgeException(String message) {
      super(message);
  }

}
TooYoungException.java
package org.o7planning.tutorial.exception.basic;

public class TooYoungException extends AgeException {

 public TooYoungException(String message) {
     super(message);
 }

}
TooOldException.java
package org.o7planning.tutorial.exception.basic;

public class TooOldException extends AgeException {

 public TooOldException(String message) {
     super(message);
 }

}
And AgeUtils class with static methods for the examination of age.
AgeUtils.java
package org.o7planning.tutorial.exception.basic;

public class AgeUtils {

 
   // This method checks the age.
   // If age is less than 18, the method will throw an exception TooYoungException
   // If age greater than 40, the method will throw an exception TooOldException
   public static void checkAge(int age) throws TooYoungException,
           TooOldException {
       if (age < 18) {

           // If age is less than 18, an exception will be thrown
           // This method ends here.
           throw new TooYoungException("Age " + age + " too young");
       } else if (age > 40) {

           // If age greater than 40, an exception will be thrown.
           // This method ends here.
           throw new TooOldException("Age " + age + " too old");
       }

       // If age is between 18-40.
       // This code will be execute.
       System.out.println("Age " + age + " OK!");
   }
}

Checked Exception and Unchecked Exception:

  • AgeException are subclasses of Exception, TooOldException and TooYoungException 2 direct subclasses of AgeException,so they are "Checked Exception"
  • Trong method AgeUtils.checkAge(int) có ném ra ngoài các ngoại lệ này vì vậy trên khai báo của method bạn cần phải liệt kê chúng thông qua từ khóa "throws". Hoặc bạn có thể khai báo ném ra ở mức tổng quát hơn
    • throws Exception.
  • Tại các nơi sử dụng AgeUtils.checkAge(int) cũng phải có sử lý để bắt các ngoại lệ đó, hoặc tiếp tục ném ra vòng ngoài.
"Checked exception" will be checked by "Java Compiler".
You have two choices:
TryCatchDemo1.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo1 {

   public static void main(String[] args) {


       System.out.println("Start Recruiting ...");
       // Check age
       System.out.println("Check your Age");
       int age = 50;

       try {

           AgeUtils.checkAge(age);

           System.out.println("You pass!");

       } catch (TooYoungException e) {

           // Do something here ..
           System.out.println("You are too young, not pass!");
           System.out.println(e.getMessage());

       } catch (TooOldException e) {

           // Do something here ..
           System.out.println("You are too old, not pass!");
           System.out.println(e.getMessage());

       }

   }
}
In the following example, we will catch exceptions through parent exceptions (super exception class).
TryCatchDemo2.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo2 {

   public static void main(String[] args) {

       System.out.println("Start Recruiting ...");
       // Check age
       System.out.println("Check your Age");
       int age = 15;

       try {

           // Here can throw TooOldException or TooYoungException
           AgeUtils.checkAge(age);

           System.out.println("You pass!");

       } catch (AgeException e) {
         
           // If an exception occurs, type of AgeException
           // This catch block will be execute
           System.out.println("Your age invalid, you not pass");
           System.out.println(e.getMessage());

       }
   }
}
You can also group different exceptions to the same block to handle if they have similar handling ways on your logic program.
TryCatchDemo3.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchDemo3 {

   public static void main(String[] args) {

       System.out.println("Start Recruiting ...");
       // Check age
       System.out.println("Check your Age");
       int age = 15;

       try {

           // Here can throw TooOldException or TooYoungException
           AgeUtils.checkAge(age);

           System.out.println("You pass!");

       } catch (TooYoungException | TooOldException e) {
           // Catch multi exceptions in one block.

           System.out.println("Your age invalid, you not pass");
           System.out.println(e.getMessage());

       }
   }

}

4- try-catch-finally

We have got accustomed with catching error through try-catch block.  Try-catch-finally is used to fully handle exception.
try {

   // Do something here

} catch (Exception1 e) {

   // Do something here

} catch (Exception2 e) {

   // Do something here

} finally {

   // Finally block is always executed
   // Do something here

}
TryCatchFinallyDemo.java
package org.o7planning.tutorial.exception.basic;

public class TryCatchFinallyDemo {

   public static void main(String[] args) {

       String text = "001234A2";

       int value = toInteger(text);

       System.out.println("Value= " + value);

   }

   public static int toInteger(String text) {
       try {

           System.out.println("Begin parse text: " + text);

           // An Exception can throw here (NumberFormatException).
           int value = Integer.parseInt(text);

           return value;

       } catch (NumberFormatException e) {

         
           // In the case of 'text' is not a number.
           // This catch block will be executed.            
           System.out.println("Number format exception " + e.getMessage());

           // Returns 0 if NumberFormatException occurs
           return 0;

       } finally {

           System.out.println("End parse text: " + text);

       }
   }

}
This is the flow of the program. Finally block is always executed.

5- Exception Wrapping

We need a some class participated in this example:
  • Person: Simulate a participant recruitment into the company with the information: Name, age, gender.
  • GenderException: Gender Exception.
  • ValidateException: Exception evaluate a candidate.
  • ValidateUtils: Class with static method evaluate candidates.
    • Valid if age between 18-40 and male
Person.java
package org.o7planning.tutorial.exception.wrap;

public class Person {

  public static final String MALE = "male";
  public static final String FEMALE = "female";

  private String name;
  private String gender;
  private int age;

  public Person(String name, String gender, int age) {
      this.name = name;
      this.gender = gender;
      this.age = age;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getGender() {
      return gender;
  }

  public void setGender(String gender) {
      this.gender = gender;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }
}
GenderException.java
package org.o7planning.tutorial.exception.wrap;

// Gender Exception.
public class GenderException extends Exception {

    public GenderException(String message)  {
        super(message);
    }
}
Class ValidateException wrap other Exception.
ValidateException.java
package org.o7planning.tutorial.exception.wrap;

public class ValidateException extends Exception {
 
  // Wrap an Exception
  public ValidateException(Exception e)  {
      super(e);
  }

}
ValidateUtils.java
package org.o7planning.tutorial.exception.wrap;

import org.o7planning.tutorial.exception.basic.AgeUtils;

public class ValidateUtils {

   public static void checkPerson(Person person) throws ValidateException {
       try {

           // Check age.
           // Valid if between 18-40
           // This method can throw TooOldException, TooYoungException.        
           AgeUtils.checkAge(person.getAge());

       } catch (Exception e) {
         
           // If not valid
           // Wrap this exception by ValidateException, and throw
           throw new ValidateException(e);

       }

       // If that person is Female, ie invalid.
       if (person.getGender().equals(Person.FEMALE)) {

           GenderException e = new GenderException("Do not accept women");
           throw new ValidateException(e);

       }
   }

}
WrapperExceptionDemo.java
package org.o7planning.tutorial.exception.wrap;

public class WrapperExceptionDemo {

   public static void main(String[] args) {
       
       // One participant recruitment.
       Person person = new Person("Marry", Person.FEMALE, 20);

       try {

           // Exceptions may occur here.
           ValidateUtils.checkPerson(person);

       } catch (ValidateException wrap) {

           // Get the real cause.
           // May be TooYoungException, TooOldException, GenderException
           Exception cause = (Exception) wrap.getCause();

           if (cause != null) {
               System.out.println("Not pass, cause: " + cause.getMessage());
           } else {
               System.out.println(wrap.getMessage());
           }

       }
   }

}

6- RuntimeException and subclasses

RuntimeException class and its subclasses are all   "Unchecked exceptions". It is not checked by Java compiler in the compile-time. In some cases, you can write your own exceptions inherited from this branch.

Below is some classes belonging to RuntimeException branch (Of course, that's not all).
Some examples handling this type of exceptions:

6.1- NullPointerException

This is one of the most common exceptions that usually causes error to the program. Exception is thrown out when you call method or access to fields of an null object.
NullPointerExceptionDemo.java
package org.o7planning.tutorial.exception.runtime;

public class NullPointerExceptionDemo {

   // For example, here is a method that can return null string.
   public static String getString() {
       if (1 == 2) {
           return "1==2 !!";
       }
       return null;
   }

   public static void main(String[] args) {

       // This is an object that references not null.
       String text1 = "Hello exception";

       // Call the method retrieves the string length.
       int length = text1.length();

       System.out.println("Length text1 = " + length);

       // This is an object that references null.
       String text2 = getString();
     
       // Call the method retrieves the string length.
       // NullPointerException will occur here.
       // It is an exception occurs at runtime (type of RuntimeException)
       // Javac compiler does not force you to use a try-catch block to handle it
       length = text2.length();

       System.out.println("Finish!");
   }

}
Results of running the example:
In reality, like handling other exceptions, you can use try-catch to catch and handle this exception. However, that is mechanical, normally, we should check to ensure that the object not null value before using it.

You can correct the above code to make it similar to the following one with the avoidance of  NullPointerException:
// This is a null object.
String text2 = getString();

// Check to make sure 'Text2' are not null.
// Instead of using try-catch.
if (text2 != null) {
  length = text2.length();
}

6.2- ArrayIndexOfBoundException

This exception occurs when you try to access to the element whose index are invalid on array. For example, an array has 10 elements, but you access to the element with index 20.
ArrayIndexOfBoundsExceptionDemo.java
package org.o7planning.tutorial.exception.runtime;

public class ArrayIndexOfBoundsExceptionDemo {

   public static void main(String[] args) {

       String[] strs = new String[] { "One", "Two", "Three" };

       // Access to the element has index 0.
       String str1 = strs[0];

       System.out.println("String at 0 = " + str1);

     
       // Access to the element has index 5.
       // ArrayIndexOfBoundsException occur here.
       String str2 = strs[5];

       System.out.println("String at 5 = " + str2);

   }

}
To avoid ArrayIndexOfBoundsException you should check the array instead of using try-catch.
if (strs.length > 5) {
   String str2 = strs[5];
   System.out.println("String at 5 = " + str2);
} else {
   System.out.println("No elements with index 5");
}