The Exception Handling in Java is one of the powerful mechanism to handle the runtime errors so that the normal flow of the application can be maintained.
An exception is an abnormal condition. In Java, an exception is an event that disrupts the normal flow of the program. It is an object which is thrown at runtime.
Runtime errors like ClassNotFoundException, IOException, SQLException, RemoteException, etc. are typically handled by exception handling.
Exception is a class provided by Java programmers to handle the worst-case situation that occurs during the execution of the program. In other words, we can say that an exception is an object that displays some meaningful messages due to which programs hang.
Whenever an exception occurs or any error is caused in runtime, the exceptions are related to the objects in Java. The Java Exception arises at the time of object creation.
To implement exception handling in Java, the compiler provides one package, which is a default package that includes the Exception class.
java.lang.Exception.*;
Hierarchy of Java Exception Classes
In Java, the main class for exception handling is Throwable, which is the parent class of both the exception and Error classes.
- An error is a type of exception that is not expected to be caught under normal circumstances by your program.
Types of Java Exceptions
- There are mainly two types of exceptions: checked and unchecked. Here, an error is considered as the unchecked exception. According to Oracle, there are three types of exceptions:
- Checked Exception
- Unchecked Exception
- Error
Checked Exception
- The classes that directly inherit Throwable class except RuntimeException and Error are known as checked exceptions. Checked Exceptions are checked at compile time.
- Example: IOException, SQLException etc.
Unchecked Exception
- The classes that inherit RuntimeException are known as unchecked exceptions. Unchecked exceptions are not checked at compile time. But they are checked at runtime.
- Example: ArrayIndexOutOfBoundsException, NullPointerException, ArithmeticException etc.
Error
- Error is irrecoverable
- e.g. OutOfMemoryError, VirtualMachineError, AssertionError etc.
The difference between Error and exception is as follows:
- Error: An Error indicates a serious problem that a reasonable application should not try to catch.
- Exception: An exception indicates conditions that a reasonable application might try to catch.
Exceptions in java is basically divided into two categories:
1. Build-In Exceptions
These are the types of exceptions that can be caught using already-existing Java libraries. It is also known as an unchecked exception or runtime Exception.
- Arithmetic Exception
- ClassNotFoundException
- IOException
- ArrayIndexOutOfBoundsException
- FileNotFoundException
- NullPointerException
- NoSuchFieldException
- NoSuchMethodException
- StringIndexOutOfBoundsException
- RuntimeException
- NumberFormatException
- InterruptedException etc.
Code Without Exception Handling (Build-In Exception)
class Exc0 {
public static void main(String args[]) {
int d = 0;
int a = 42/d;
}
}
java.lang.ArithmeticException: / by zero at Exc0.main(Exc0.java:4)
When the Java runtime system detects the attempt to divide by zero, it constructs a new exception object and then throws this exception. This causes the execution of Exc0 to stop, because once an exception has been thrown, it must be caught by an exception handler and dealt with immediately. In this example, we haven’t supplied any exception handlers of our own, so the exception is caught by the default handler provided by the Java runtime system.
2. User-Defined Exception
- These are the types of exceptions that can be caught using some of the customised exceptions created by the user, and the user should have the ability to handle these exceptions. These exceptions can also be called checked exceptions or compile-time exceptions.
// File Name InsufficientFundsException.
import java.io.*;
public class InsufficientFundsException extends Exception { private double amount;
public InsufficientFunds Exception (double amount) { this.amount = amount;
}
public double getAmount() { return amount;
}
}
Here, InsufficientFundsException is user defined exception class.
Basic Syntax Of Exceptional Handling:
try{
// block of code to monitor for errors
}
catch (Exception Type1 exOb) {
// exception handler for Exception Typel
}
catch (Exception Type2 exOb) {
// exception handler for Exception Type2
}
// ...
finally {
// block of code to be executed after try block ends
}
Handling Exceptions:
There are five keywords used to handle exceptions in Java:
- try
- catch
- finally
- throw
- throws
1. try :
The try
statement allows you to define a block of code to be tested for errors while it is being executed.
2. catch :
The catch
statement allows you to define a block of code to be executed, if an error occurs in the try block.
// Syntax
try {
// Block of code to try
}
catch(Exception e) { // e - object
// Block of code to handle errors
}
Note: The
try
andcatch
keywords come in pairs, and there is a single "try" and "finally" block and a multiple catch block.
Consider the following example:
public class Main {
public static void main(String[ ] args) {
int[] myNumbers = {1, 2, 3};
System.out.println(myNumbers[10]); // error!
}
}
// This will generate an error, because myNumbers[10] does not exist.
The output will be something like this:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
at Main.main(Main.java:4)
If an error occurs, we can use try...catch
to catch the error and execute some code to handle it:
public class Main {
public static void main(String[ ] args) {
try {
int[] myNumbers = {1, 2, 3};
System.out.println(myNumbers[10]);
} catch (Exception e) {
System.out.println("Something went wrong.");
}
}
}
The output will be:
Something went wrong.
3. Finally :
The finally
statement lets you execute code, after try...catch
, regardless of the result:
public class Main {
public static void main(String[] args) {
try {
int[] myNumbers = {1, 2, 3};
System.out.println(myNumbers[10]);
} catch (Exception e) {
System.out.println("Something went wrong.");
} finally {
System.out.println("The 'try catch' is finished.");
}
}
}
The output will be:
Something went wrong.
The 'try catch' is finished.
4. The throw keyword :
The throw
statement allows you to create a custom error.
The throw
statement is used together with an exception type. There are many exception types available in Java: ArithmeticException
, FileNotFoundException
, ArrayIndexOutOfBoundsException
, SecurityException
, etc:
Example
Throw an exception if age is below 18 (print “Access denied”). If age is 18 or older, print “Access granted”:
public class Main {
static void checkAge(int age) {
if (age < 18) {
throw new ArithmeticException("Access denied - You must be at least 18 years old.");
}
else {
System.out.println("Access granted - You are old enough!");
}
}
public static void main(String[] args) {
checkAge(15); // Set age to 15 (which is below 18...)
}
}
The output will be:
Exception in thread "main" java.lang.ArithmeticException: Access denied - You must be at least 18 years old.
at Main.checkAge(Main.java:4)
at Main.main(Main.java:12)
If age was 20, you would not get an exception:
Example
checkAge(20);
The output will be:
Access granted - You are old enough!
5. throws :
The throws
keyword indicates what exception type may be thrown by a method.
There are many exception types available in Java: ArithmeticException
, ClassNotFoundException
, ArrayIndexOutOfBoundsException
, SecurityException
, etc.
Differences between throw
and throws
:
Examples: Most Common Java Exceptions
1. ClassNotFoundException
If any class is not defined properly then it will result into ClassNotFoundException.
package com.journaldev.exceptions;
public class DataTest {
public static void main(String[] args) {
try {
Class.forName("com.journaldev.MyInvisibleClass");
ClassLoader.getSystemClassLoader().loadClass("com.journaldev.MyInvisibleClass");
ClassLoader.getPlatformClassLoader().loadClass("com.journaldev.MyInvisibleClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Note that com.journaldev.MyInvisibleClass
doesn’t exist, so When we execute above program, we get following exception stack trace.
java.lang.ClassNotFoundException: com.journaldev.MyInvisibleClass
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:292)
at com.journaldev.exceptions.DataTest.main(DataTest.java:7)
In above example, all the three statements will throw java.lang.ClassNotFoundException
.
It’s very easy to fix ClassNotFoundException because the exception stack trace clearly specifies the class not found. Just check for class path settings and make sure class it’s present at runtime.
2. ArrayIndexOutOfBoundsException
Whenever any wrong index is accessed and the range of the index is unreachable and cannot be accessed, it comes out to be an ArrayIndexOutOfBoundsException.
public class ArrayIndexOutOfBoundsExceptionExample {
public static void main(String[] args) {
String[] arr = new String[10];
System.out.println(arr[11]);
}
}
In this example, a String
array of length 10 is created. An attempt is then made to access an element at index 10, which falls outside the range of the array, throwing an ArrayIndexOutOfBoundsException
:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
at ArrayIndexOutOfBoundsExceptionExample.main(ArrayIndexOutOfBoundsExceptionExample.java:4)
To avoid the ArrayIndexOutOfBoundsException
, the following should be kept in mind:
- The bounds of an array should be checked before accessing its elements.
- An array in Java starts at index
0
and ends at indexlength - 1
, so accessing elements that fall outside this range will throw anArrayIndexOutOfBoundsException
. - An empty array has no elements, so attempting to access an element will throw the exception.
- When using loops to iterate over the elements of an array, attention should be paid to the start and end conditions of the loop.
3. NullPointerException
The NullPointerException
occurs due to a situation in application code where an uninitialized object is attempted to be accessed or modified. Essentially, this means the object reference does not point anywhere and has a null value.
Some of the most common scenarios for a NullPointerException
are:
- Calling methods on a null object
- Accessing a null object’s properties
- Accessing an index element (like in an array) of a null object
- Passing null parameters to a method
- Incorrect configuration for dependency injection frameworks like Spring
- Using
synchronized
on a null object - Throwing null from a method that throws an exception.
public class NullPointerExceptionExample {
private static void printLength(String str) {
System.out.println(str.length());
}
public static void main(String args[]) {
String myString = null;
printLength(myString);
}
}
/* Exception in thread "main" java.lang.NullPointerException
at NullPointerExceptionExample.printLength(NullPointerExceptionExample.java:3)
at NullPointerExceptionExample.main(NullPointerExceptionExample.java:8) */
In this example, the printLength()
method calls the length()
method of a String without performing a null check prior to calling the method. Since the value of the string passed from the main()
method is null, running the above code causes a NullPointerException.
To fix the NullPointerException
in the above example, the string should be checked for null or empty values before it is used any further:
import org.apache.commons.lang3.StringUtils;
public class NullPointerExceptionExample {
private static void printLength(String str) {
if (StringUtils.isNotEmpty(str)) {
System.out.println(str.length());
} else {
System.out.println("Empty string");
}
}
public static void main(String args[]) {
String myString = null;
printLength(myString);
}
}
The code is updated with a check in the printLength()
method that makes sure the string is not empty using the apache commons StringUtils.isNotEmpty()
method. Only if the string is not empty the length()
method of the string is called, else it prints the message Empty string
to console.
4. ClassCastException
ClassCastException
is a runtime exception that occurs when the application code attempts to cast an object to another class of which the original object is not an instance.
For example, a String object cannot be cast to an Integer object and attempting to do so will result in a ClassCastException
. Since the ClassCastException
is an unchecked exception, it doesn't need to be declared in the throws clause of a method or constructor.
Some of the most common sources of ClassCastException
in Java are:
- Using collections like HashMap, ArrayList and HashTable which do not use Java Generics.
- Using methods which were written on interfaces prior to Java 5 that use polymorphism.
- Not using type-safety when casting objects in Java.
Example of a ClassCastException
thrown when a String is attempted to be cast to an Integer:
public class NullPointerExceptionExample {
private static void printLength(String str) {
System.out.println(str.length());
}
public static void main(String args[]) {
String myString = null;
printLength(myString);
}
}
/* Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer
at ClassCastExceptionExample.main(ClassCastExceptionExample.java:4) */
Here, the String obj
is attempted to be cast to an Integer. Since it is not an instance of the Integer class, this operation throws a ClassCastException.
To fix the ClassCastException
in the above example, the object type should be checked before performing the cast operation:
public class ClassCastExceptionExample {
public static void main(String[] args) {
Object obj = new String("Hello");
if (obj instanceof Integer) {
System.out.println((Integer) obj);
} else {
System.out.println(obj);
}
}
}
// Output: Hello
The code is updated with a check that makes sure obj
is an instance of the Integer class before it is cast to an Integer. The cast operation is only done if the check passes. The above code runs successfully without throwing a ClassCastException.