Exception Handling in Java

Exception Handling in Java

[16]

Introduction

An exception or exceptional event is a problem that arises during the execution of a program. When an exception occurs the normal flow of the program is disrupted and the program/application terminates abnormally, therefore these exceptions need to be handled.

A Java Exception is an object that describes the exception that occurred in a program. When an exceptional event occurs in Java, an exception is said to be thrown. An exception can occur for many different reasons like when a user enters invalid data or when a file that needs to opened cannot be found or when a network connection gets lost abruptly or when the JVM runs out of memory. Mainly, exceptions are caused by users, programmers or when physical resources fail. The Exception Handling in Java is one of the robust mechanisms to handle the exceptions or runtime errors so that the normal flow of the application can be maintained.

In Java there are three categories of Exceptions:

  1. Checked Exceptions: A checked exception is an exception that occurs at the compile time, these are also called as compile time exceptions. For example, IOException, SQLException, etc.

  2. Runtime Exceptions: An unchecked exception that occurs during the execution, these are also called as runtime exceptions. These include programming bugs, such as logic errors or improper use of an API. Note, runtime exceptions are ignored at the time of compilation. For example, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException, etc.

  3. Errors: These are not exceptions at all, but problems that arise beyond the control of the user or the programmer. For example, OutOfMemoryError, VirtualMachineErrorException.

All exception classes are subtypes of the java.lang.Exception class. The Exception class is a subclass of the Throwable class. Other than the Exception class there is another subclass called Error class which is derived from the Throwable class.

Error vs/ Exception

In Java, errors and exceptions are both throwable objects, but they represent different types of problems that can occur during program execution. Errors are usually caused by serious problems outside the control of the program, such as running out of memory or a system crash. Errors are represented by the Error class and its subclasses. Common examples of errors in Java include:

  • OutOfMemoryError: Thrown when the Java Virtual Machine (JVM) runs out of memory.

  • StackOverflowError: Thrown when the call stack overflows due to too many method invocations.

  • NoClassDefFoundError: Thrown when a required class cannot be found.

Exception Handling Mechanism

Exception Handling is done using five keywords —

  1. try

  2. catch

  3. finally

  4. throw

  5. throws

Using Try-catch

  • try: The Java try block is used to enclose code that might throw an exception. It must be within the method. The try block must be followed by either catch or finally block.

  • catch: The catch block in Java is used to handle the exception. It must be used after the try block only. The catch block that follows the try block is checked, if the type of exception that has occurred is listed in that catch block then the exception is handed over to the catch block that handles it.

Syntax

try {
    // Risky code
} catch(ExceptionName1 e) {
    // Catch Block 1
} catch(ExceptionName2 e) {
    // Catch Block 2
}

In the above syntax, there are two catch blocks. In try block, we write the code that might generate exception. If the exception is generated by the protected code then the exception is thrown to the first catch block. If the type of the exception thrown matches ExceptionName1, it is caught there and executed in the catch block. Else, the exception gets passed down to the second catch block. This continues until the exception is either caught or falls through all catches, in that case the current method stops execution.

Example

public class JavaDemo {
    public static void main(String[] args) {
        try {
            int arr[] = {1, 2, 3};
            arr[3] = 3 / 0;
        } catch(ArithmeticException e) {
            System.out.println("You are trying to divide a number by 0 " + e);
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("You are trying to access an array element which does not exist " + e);
        }
    }
}

Output

You are trying to divide a number by 0 java.lang.ArithmeticException: / by zero

There's a chance you might be wondering why only one of the exceptions was caught when both the exceptions are valid and should have been caught. In Java, when an exception is thrown, the program control jumps to the corresponding catch block, and then it exits the try-catch block entirely. It does not continue to the next catch block or resume the try block to potentially throw another exception. This means only the first exception encountered will be caught and handled.

In the code, the exception for dividing by zero was encountered first, so it was caught by the ArithmeticException catch block. Since the exception was caught and handled, the program did not proceed to the next line arr[3] = ... where the ArrayIndexOutOfBoundsException would occur.

Nested Try-catch Blocks

In Java, a try block within a try block is known as Nested try block. Nested try blocks are used when a part of a block may cause one error while entire block may cause another error. In that case, if the inner try block does not have a catch handler for a particular exception then the outer try is checked for match. This continues until one of the catch succeeds or until the entire nested try statements are done. If no catch statements match, then the Java run-time system will handle the exception.

Syntax

try {
    try {
        // Risky code
    } catch(ExceptionName1 e) {
        // Catch Block 1
    }
} catch(ExceptionName2 e) {
    // Catch Block 2
}

Example

public class JavaDemo {
    public static void main(String[] args) {
        try {
            int arr[] = {5, 0, 1, 2};
            try {
                arr[4] = arr[3] / arr[1];
            } catch(ArithmeticException e) {
                System.out.println("You are trying to divide a number by 0 " + e);
            }
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("You are trying to access an array element which does not exist " + e);
        } catch(Exception e) {
            System.out.println("Generic Exception " + e);
        }
        System.out.println("We're out of 'try-catch' block now");
    }
}

Output

You are trying to divide a number by 0 java.lang.ArithmeticException: / by zero
We're out of 'try-catch' block now

Finally

A finally keyword is used to create a block of code that follows a try-catch block. A finally block always executes regardless of whether the exception has occurred or not and is usually placed at the end of the catch block.

Syntax

try {
    // Risky code
} catch(ExceptionName1 e) {
    // Catch Block 1
} catch(ExceptionName2 e) {
    // Catch Block 2
} finally {
    // This block will always execute
}

Example

public class JavaDemo {
    public static void main(String[] args) {
        int a[] = new int[2];
        try {
            System.out.println("Accessing element three " + a[3]);
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Exception thrown " + e);
        } finally {
            a[0] = 10;
            System.out.println("The first element's value is " + a[0]);
            System.out.println("This block is always executed.");
        }
        System.out.println("Out of the loop... try-catch-finally!");
    }
}

Output

Exception thrown java.lang.ArrayIndexOutOfBoundsException: 3
The first element's value is 10
This block is always executed.
Out of the loop... try-catch-finally!

Some pointers to keep in mind.

  • A catch clause cannot exist without a try statement.

  • Adding a finally clause for every try-catch block is not mandatory.

  • The try block cannot be present without either catch or finally clause.

Throw

When we explicitly want to throw an exception we make use of the throw keyword. We can throw either checked or unchecked exceptions using this keyword. But, only objects of the Throwable class or it's subclasses can be thrown. On encountering a throw statement, the program execution ceases and the closest catch statement is checked for matching type of the exception.

Syntax

throw new ThrowableInstance("Demo");

Example

public class JavaDemo {
    public static void main(String[] args) {
        demo();
    }

    public static void demo() {
        try {
            throw new ArithmeticException("Demo");
        } catch(ArithmeticException e) {
            System.out.println("Exception Caught");
        }
    }
}

Output

Exception Caught

Throws

The throws keyword is used to declare an exception. If a method does not handle a checked exception, the method must declare it using the throws keyword. This keyword appears at the end of the method's signature. You are allowed declare multiple exceptions.

Syntax

return_type methodName() throws exceptionClassName {  
    // Method Code  
}

Example

public class JavaDemo {
    public static void main(String[] args) {
        try {
            demo();
        } catch(ArithmeticException e) {
            System.out.println("Exception Caught");
        }
    }

    public static void demo() throws ArithmeticException {
        System.out.println("We're inside the demo method.");
        throw new ArithmeticException("Demo");
    }
}

Output

We're inside the demo method.
Exception Caught