Exception Handling in Java

In this article, we will be learning about exceptions in Java, what they are, how do they arise, and how to handle them for the smooth flow of the program. Exception handling is one of the most important features of Java and one of the reasons why it is considered such a powerful language. So, let’s begin with the definition of an Exception. 

What is an Exception in Java?

An exception is an unwanted or unexpected event that occurs at the time of execution. It disrupts the normal flow of the program which often leads to termination of the program or application.

Exceptions in Java can be caused due to several reasons like trying to access an element of an array with an index greater than the size of the array, trying to access a non-existing file in your program, invalid data input, network problems, etc. Fortunately, Java has a robust system for handling such errors to prevent the termination of the program.

Difference between Error and Exception

An error is something that cannot and should not be handled by the developer. It usually tends to signal the end of your program, cannot be recovered from and causes you to exit the program altogether instead of trying to handle it.

Exceptions can and should be handled by the developer, otherwise, they will surely lead to abnormal termination of the program. They are objects of a type derived from System. Exception class and arise when non-fatal and recoverable errors occur during run-time.

What is Exception handling in Java?

Whenever an exception occurs in Java, an object known as the Exception object is created and handed off to the Java-Runtime System(Java Virtual Machine). The entire program is searched for an exception handler of the type of exception that has occurred. If it finds none, then the default exception handler prints a message to the user regarding the details of the exception that has occurred. Let us take a simple example of an exception, dividing a number by zero.

class Exceptions {
    public static void main(String args[]) {
        int n = 0;
        double quotient = 234/n;
        System.out.println("Quotient: " + quotient);
    }
}


Compile and execute the above-mentioned code. You will notice that it is successfully compiled but shows an error after trying to execute. Exceptions occur at runtime, therefore, the code was successfully compiled and the compiler found no error in it. But the JVM caught an exception and displayed a message like this-

Exception in thread "main" java.lang.ArithmeticException: / by zero
         at Exceptions.main(Exceptions.java:4)


This message is quite non-user-friendly and won’t make any sense to a lot of users. Thus, we use exception handling to display a user-friendly and understandable message to the users so that they will be able to figure out what went wrong. Now let us see how we can handle the above exception-

class Exceptions {
    public static void main(String args[]) {
        int n = 0;
        try {
            double quotient = 234/n;
            System.out.println("Quotient: " + quotient);
        }
        catch(ArithmeticException e) {
            System.out.println("Division by zero has occurred");
        }
    }
}


I have added the try and catch blocks and handled the exception here. We will study in detail about these blocks in the coming section of the article. For now, you can just copy-paste the code and compile & run it. You will now see a user-friendly and understandable message that you have written for the exception – “Division by zero has occurred”.

In this way, we have successfully handled the exception. If the exception is not handled properly, an unfriendly message containing the details of the exception object will be printed and the program will be terminated abruptly and abnormally. But if the exception is handled properly by the developer then the rest of the program will be executed with a friendly message being displayed. This is the main advantage of Exception Handling.

Types of Exceptions

In Java, we have two types of exceptions-

  1. Checked Exceptions: These are the exceptions that arise during compilation time. The compiler checks for these exceptions and whether the programmer has handled them. If they are not handled, it causes compilation error and the program will not be compiled. Examples of checked exceptions are classNotFoundException, SQLException, IOException, etc. 

  2. Unchecked Exceptions: These are the exceptions that arise during run-time. The program will get compiled but not get executed. The developer has to handle these exceptions. Examples of unchecked exceptions: ArrayOutOfBoundException, ArithmeticException, NullPointerException, etc.

Keywords

Now let us study the keywords used in Exception Handling in detail.
The Try Block: The first essential step to exception handling is to enclose the lines which can cause an exception, within the try block. The try block contains those lines which can cause an exception and can have one or more legal lines of code. If an exception occurs at a particular statement of the try block, the rest of the statements will not be executed. Thus, it is recommended to exclude those lines which won’t throw an exception. The try block syntax is-

try {
  Block of code;
 }
 catch and finally blocks…


A try block must always be followed by a catch block or finally block or both.

The Catch Block:  The catch block is required to handle the exception. It contains lines of code that will be executed if an exception occurs. Each catch block is an exception handler that handles the type of exception specified by the argument. The argument type ExceptionType declares the type of exception that the handler can handle and must be the name of a class that inherits from the Throwable class.

The Throwable class is the super-class of all the errors and exceptions in Java. In the previous example, the class ArithmeticException is an instance of Throwable class and is thrown by the JVM to handle the arithmetic exceptions. To declare the exception type that the catch block will handle, we need to know the type of Exception that can be thrown by the JVM in the try block. The syntax of the catch block is-

try {
code;
}
catch(ExceptionType e1) {
code;
} 
catch(ExceptionType e2) {
 code;
}


The try block has to be followed by a catch block. No lines of code can come in between. There may be multiple catch blocks to one try block. Each catch block handles different types of exceptions. The catch block is executed as and when the exception matching the exception type in the handler is invoked.

 A few points to be remembered for the try-catch block are:

  1. A single try block can have multiple catch blocks and the try block contains only those statements which can cause an exception.
  2. A generic catch block that does not have any specific exception handling type can handle all types of exceptions.
  3. Corresponding catch blocks are executed for that specific type of exception. Example- catch(ArithmeticException e) will handle only arithmetic exceptions, catch(NullPointException e) will handle only NullPointExceptions. 
  4. If no exception occurs, then all the catch blocks will be completely ignored.

An example to demonstrate the use of multiple catch blocks is-

class Exceptions {
    public static void main(String args[]) {
        int n = 0;
        int A[] = {1,2,3,4,5};
        try {
            System.out.println("Element: " + A[7]);
            double quotient = 234/n;
            System.out.println("Quotient: " + quotient);
        }
        catch(ArithmeticException e) {
            System.out.println("Division by zero has occurred");
        }
        catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("Out of bound index has occurred");
        }
        catch(Exception e) {
            System.out.println("Some other exception has occurred");
        }
    }
}


Finally block: The lines contained inside the finally block will be executed whether or not an exception occurs. This block is written after the end of all the catch blocks. This block is not just useful in handling exceptions but also helps the programmer to avoid cleaning up the code that may have been bypassed by a continue, return or break statement e.g. we can statements that close the database connection. So that even if there is any exception, we are sure that database connection will get closed. Finally syntax-

finally {
   System.out.println("Exited from the try-catch block");
}


Throw: For a catch block to handle an exception, it must be thrown by someone or something. Either the programmer throws it or the JVM does it. But in either case, the keyword throw is used. We have seen that an instance of the Throwable class has been thrown whenever an exception arises. By using the throw keyword, we can create our own set of conditions to throw an exception explicitly. We use this keyword to throw custom exceptions. The syntax and an example of the throw keyword is-

throw instanceOfThrowableClass; // syntax
throw new NullPointerException("Null pointer Exception"); //example


Remember that we can throw only those classes which are instances of the Throwable class like ArithmeticException, ArrayIndexOutOfBoundException, etc. We can also create our own exception classes and throw them but we will learn about that later in this article. For now, remember that we can throw only those objects which have been inherited from the Throwable class. An example to find out whether a student is eligible to sit for an exam based on his/her age. If he/she is older than 15 years, he/she will be eligible for the exam otherwise not. Here, we can explicitly throw an exception with a suitable message with a condition based on age. Here’s the code.

public class ThrowExample {
   static void checkEligibilty(int age){ 
    System.out.println("Age: " + age);
    try{
      if(age<15) {
         throw new ArithmeticException("The Student is not eligible for examination"); 
      }
      else {
         System.out.println("The Student is Eligible!"); 
      }
    }
    catch(ArithmeticException e) {
      System.out.println(e.getMessage());
    }
   } 

   public static void main(String args[]){ 
     System.out.println("Let's find out your eligibility!");
     checkEligibilty(12); 
     System.out.println("Thank you"); 
 } 
}


The output will be-

Let's find out your eligibility!
Age: 12
The Student is not eligible for examination
Thank you


In the above code, we have explicitly thrown an ArithmeticException with a message under a condition. We have enclosed it in a try-catch block and display the message of the exception using the in-built method getMessage(). In this way, we can define custom errors using the throw keyword.

Throws: The throws keyword is used to indicate that a particular method may throw an exception of a specific type. The caller of this method has to handle the exception using a try-catch block. The throws keyword declares an exception and if not handled, could cause compilation errors. This keyword is used to handle only checked exceptions, i.e., compile-time exceptions. Examples of using throws keyword are-

import java.io.*;
class ThrowsExample {
    public static void main(String args[]) throws IOException {
        throw new IOException("Exception has arised.");
    }
}


In the above example, we used the throws keyword in the signature of the main() thus, JVM directly handled the exception we explicitly invoked. Another example-

import java.io.*;
class Example {
    public void artOfTesting(int n) throws IOException, ClassNotFoundException{
        System.out.println("Inside artOfTesting");
        if(n<0)
            throw new IOException("IOException occurred");
        else
            throw new ClassNotFoundException("ClassNotFoundException occurred");
    }
}


class ThrowsExample {
    public static void main(String args[]) {
        Example E = new Example();
        try {
            E.artOfTesting(-1);
        }
        catch(IOException e) {
            System.out.println(e.getMessage());
        }
        catch(ClassNotFoundException e) {
            System.out.println(e.getMessage());
        }
        finally {
            System.out.println("Inside main method");
        }
    }
}


In the above example, I have used two Exceptions with the method artOfTesting() which is allowed in Java with the use of commas. 

User-defined Exceptions

As mentioned earlier, we can create our own exception classes. Now we will learn how. These are known as user-defined or custom exceptions which we create and throw on a particular condition. A user-defined exception class is created by extending it from the Exception class in Java. Example-

class MyException extends Exception {
    public MyException(String s){
        super(s);
    }
}
class CustomExceptions {
    public static void main(String args[]) throws MyException{
        try {
            throw new MyException("Custom Exception has occurred");
        }
        catch(MyException e) {
            System.out.println(e.getMessage());
        }
    }
}


Calling the super() here is not mandatory but a good practice. The constructor takes a String as an argument. But this is also not mandatory and we can leave it null. The above code will produce the following output – “Custom Exception has occurred”.

Common Exceptions

A few common exceptions that frequently arise while programming in Java are-

  1. ArithmeticException: This exception occurs in case of any unfavorable arithmetic condition e.g. when we divide an integer with zero.

  2. ArrayIndexOutOfBoundsException: This exception occurs when we try to access a non-existent index of an array. 

  3. NumberFormatException: This exception occurs when we try to convert a string to any numeric value, but the string format is illegal and cannot be converted.

  4. StringIndexOutOfBoundsException: Similar to the ArrayIndexOutOfBounds exception, this exception arises when a non-existent string index is accessed.

  5. NullPointerException: This exception occurs when an object is being created with value “null”.

  6. ClassCastException: This exception occurs when we try to improperly convert a class from one type to another.

  7. IllegalArgumentException: This exception occurs when an illegal or inappropriate argument has been passed to a method.

  8. IOException: This exception occurs when there has been a failure in the input/output operations.

  9. ClassNotFoundException: This exception occurs when the specified class cannot be found in the classpath.

  10. FileNotFoundException: This exception occurs when an attempt to open a file denoted by a specified pathname has failed.

  11. EmptyStackException: This exception arises when the pop/peek operation is performed on an empty stack.

Conclusion

We have reached the end of our article on Exception handling. By now, we know what exceptions are in Java, how are they different from errors, how to handle exceptions, types of exceptions, keywords used in exception handling, how to throw and catch exceptions, how to create custom/user-defined exceptions, and we saw a few common exceptions.

Exception Handling is an important part of Java programming since every developer needs to handle his/her exceptions to prevent the application from crashing. So, it is very essential to know how to create, throw, and handle exceptions in Java. Hope you understood all the concepts properly and will be able to apply them. Continue learning!

Leave a Comment