Exception handling in Java is one of the most important concepts in Java programming. Whenever we develop software we must ensure that we handle the java exceptions correctly. In this tutorial, we will understand about java exceptions and its types along with important terminologies that we use in exception handling.
Table of Contents
What is an exception
In Java, an exception is a condition that stops or terminates the execution process when it encounters an unexpected condition. This interrupts the normal execution flow and throws a system-generated message which the user might not understand. We can handle this situation in java by providing a meaningful message to the user when an exception occurs.
When does an exception occur
Exceptions can occur either due to human error or program error. Below are a few reasons when a java exception occurs:
- Providing bad user input data
- Opening a file that does not exist
- Accessing an array element whose index does not exist
- Bad network or network connection issues
Advantages of exception handling in Java
- Does not terminate the execution flow
- Handles the exception and proceeds with remaining program execution
Let us understand the advantage of exception handling with a simple example. Suppose we have around 100 statements to execute and an exception occurs in the 25th statement that is not handled, then it forces the program to stop the execution, and it will never execute the statements from 26 to 100. In order to overcome this, if we handle the exception that arises in the 25th statement, then instead of terminating the remaining execution, it handles the same and continues the entire program execution until the last 100th statement.
Exception hierarchy in Java
Types of Java exceptions
We can classify java exceptions into two categories:
- Checked exceptions:
- Unchecked exceptions:
Checked exceptions
We can also name them as compilation exceptions which means the compiler handles them during the code compilation. In other words, it checks the exception during compilation and warns the developer that there are chances for an exception to occur. In this case, the developer has to handle it in the code. Example: IOException, FileNotFoundException, etc
Example
Below is an example of a checked exception. Suppose we have a class inside which we try to use a FileReader class. We can get file-related exceptions if we do not handle them. Hence the compiler throws a warning to handle the exception if we do not handle it.
import java.io.*; public class CheckedExceptions { public static void main(String[] args) { String filename = "D:\\CheckedExceptions.txt"; File f = new File(filename); FileReader fr = new FileReader(f); } }
Exception in thread "main" java.lang.Error: Unresolved compilation problem: Unhandled exception type FileNotFoundException at CheckedExceptions.main(CheckedExceptions.java:8)
We can implement checked exception handling in 2 different ways:
Using throws keyword
We can use the throws keyword in the method declaration to handle the exception type. Below is an example to handle FileNotFoundException. But this is not a good approach since we are not providing any user-friendly message and it generates system messages which the user may not understand.
import java.io.*; public class CheckedExceptions { public void readFile() throws FileNotFoundException { String filename = "D:\\CheckedException.txt"; File f = new File(filename); FileReader fr = new FileReader(f); } }
Using try-catch block
We use try-catch keywords as a combination where we write the code that may produce exceptions inside the try block and mention the exception message inside the catch block. This is the best approach since we can clearly mention the exception reason inside the catch block. We will discuss in detail about try-catch block in a separate tutorial.
import java.io.*; public class CheckedExceptions { public void readFile() { String filename = "D:\\CheckedException.txt"; File f = new File(filename); try { FileReader fr = new FileReader(f); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Unchecked exceptions
We can also call them runtime exceptions since it occurs during the execution of the program. It is very important for the developer to handle these to exit safely from the code execution when it encounters an unexpected condition. Example: ArrayIndexOutOfBoundException, NullPointerException, etc.
Example
This is the most common example of unchecked exception where we try to divide a number by 0 which is actually not feasible. Runtime exceptions actually compile the code successfully and throw an exception during execution. Since we have not handled the exception in the code, we can see that the program execution terminates in the statement int result = a/0
and does not execute the remaining statements.
public class UncheckedException { int a = 5; public void divide() { int result = a/0; System.out.println(result); System.out.println("Divide method executed"); } public static void main(String[] args) { UncheckedException uce = new UncheckedException(); uce.divide(); } }
Exception in thread "main" java.lang.ArithmeticException: / by zero at UncheckedException.divide(UncheckedException.java:7) at UncheckedException.main(UncheckedException.java:13)
Handling the exception
We can implement unchecked exception handling either using throws
or try-catch blocks. In the below example we have used a try-catch block where we write the code that might produce an exception inside the try
block and write the message inside the catch
block. Now, when we execute the code, it successfully handles the exception and prints the message. Also, it executes the remaining statements in the code. If you notice, it did not execute the last print statement “Divide method executed” when we did not handle the exception.
public class UncheckedException { int a = 5; public void divide() { try { int result = a/0; System.out.println(result); } catch(ArithmeticException e) { System.out.println("Division by 0 is not allowed"); } System.out.println("Divide method executed"); } public static void main(String[] args) { UncheckedException uce = new UncheckedException(); uce.divide(); } }
Division by 0 is not allowed Divide method executed
Java Exception methods
Below are the common methods that we use during exception handling in java. The examples of these methods are based on the above example of UncheckedException.
Method | Description | Example |
---|---|---|
public String getMessage() | Returns the detailed exception message of the Throwable instance. | / by zero |
public String getCause() | Returns the cause of the Throwable instance if known, else returns null | null |
public void printStackTrace() | Prints the Throwable instance and its backtrace to the standard error stream. | java.lang.ArithmeticException: / by zero at UncheckedException.divide(UncheckedException.java:8) at UncheckedException.main(UncheckedException.java:21) |
public String toString() | Returns the classname of the exception followed by the exception message | java.lang.ArithmeticException: / by zero |
Java Exception Keywords
To implement exception handling in Java, we use either of the below 5 keywords in our code.
Exception keyword | Description |
---|---|
try | Contains the code in which exception might occur. It always precedes the catch block |
catch | Contains the code to handle the exception. It prints the exception message. It always follows the try block |
finally | It is followed by the try block and handles the important code which needs to be executed irrespective of exception occurs or not |
throws | It is used to declare exceptions in the method signature. Specifies the type of exception that the method handles. |
throw | It is used to throw an exception |
Simple Exception Handling example
Below is a simple example that illustrates exception handling in java. We declare an array of integers variable numbers and initialize with 4 values. Hence the array size is 4 with index ranging from 0 to 3. Inside arrayElements method, we print the array index value 4 within the try
block and handle the ArrayIndexOutOfBoundsException in the catch
block. In the finally
block, we just print a statement. Now when we execute this code, it successfully handles the exception and prints the error message along with the statement in the finally
block. It executes this block irrespective of whether an exception occurs or not.
public class ArrayException { int[] numbers = {5,8,1,3}; public void arrayElements() { try { System.out.println("Element at index 4: " + numbers[4]); } catch(ArrayIndexOutOfBoundsException e) { System.out.println(e.getMessage()); System.out.println("Element at specified index does not exist"); } finally { System.out.println("Handling Array Exceptions"); } } public static void main(String[] args) { ArrayException a = new ArrayException(); a.arrayElements(); } }
Index 4 out of bounds for length 4 Element at specified index does not exist Handling Array Exceptions