Exception Handling

An exception is a problem that arises during the execution of a program. An exception can occur for many different reasons. Here are some scenarios:

  • A user has entered invalid data.
  • Program tries to access an array index which is out of bounds.
  • A network connection has been lost in the middle of communications
  • Too many objects are created by the program and your machine runs out of memory.

Some of these exceptions are caused by user error, others by programmer error, and others by physical resources not available. Some exceptions a programmer can anticipate. For e.g., user input. Even though you ask the user to input only numbers, there will be at least one person who will input a string! You as a programmer should anticipate that some users will not follow instructions. In such cases, you should not crash (exit) your program. Instead you have to handle it gracefully and let the user know his mistakes and ask him to correct it.

Java gives provides broadly two types of Exceptions:

Checked exceptions:

These are exceptional conditions that a well-written application should anticipate and recover from. Common exceptions in this category; FileNoteFoundException, IOException. These exceptions are checked during compile time and you cannot compile your java program if you do not handle the anticipated exceptions in your code. You will learn handling exceptions shortly.

Unchecked Exceptions:

These are exceptional conditions that the programmer did not anticipate to happen. Errors and runtime exceptions are collectively known as unchecked exceptions.

  • Runtime exceptions: Your program compiled fine and when you are running the code exceptional conditions occurred. For e.g., when the program has an arithmetic statement in which you divide an integer by zero. In this condition, there is barely anything you can do to recover if your program depends on this statement to meaningfully compute something as output. The most common exception in this category that you will come across often is NullPointerException. This is thrown when ever the program attempts to invoke a method on a null object. Although RuntimeExceptions are unanticipated, as programmers you do try to handle many of these when ever it makes more sense not to crash the program and continue with an alternative route. Javadoc on RuntimeException: https://docs.oracle.com/javase/9/docs/api/java/lang/RuntimeException.html

Javadoc on NullPointerException: https://docs.oracle.com/javase/9/docs/api/java/lang/NullPointerException.html

  • Errors: These are exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from. For example, suppose that an application successfully opens a file for input, but is unable to read the file because of a hardware or system malfunction. The unsuccessful read will throw java.io.IOError. An application might choose to catch this exception, in order to notify the user of the problem — but it also might make sense for the program to print a stack trace and exit. Another very common Error is the one that is thrown when JVM is out of memory. In this case there is barely anything that the program can do other than printing the stack trace and exiting the program.

All exceptions are children of the top level class: java.lang.Exception.

Exception Handling

So when you decide to handle an exception instead of crashing or exiting your program, how do you do it? Java gives us try-catch-finally construct to handle exceptions


import java.util.Scanner;
public class ExceptionDemo {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        try {
            // statements which throw an exception are inside the try block
           System.out.println("Please input a number.");
           int a = scanner.nextInt();
           System.out.println("You input a number..Yay you followed instruction");
        } catch(Exception e) {
            // catch block contains statements which handle the exception
            System.out.println("Hey, you did not follow instructions! " +
             "You seem to have input something other than a pure number. " +
              "Please rerun the program by giving a valid number");

        } finally {
            // finally is optional. used to close any open resources. 
            // In our program, it is a good idea to close scanner object.
            scanner.close();
        }

    }
}

In the above program, the try block - between the two curly braces, contains any statement which could throw an Exception. The catch block contains code that should be executed when an exception is thrown. The finally block is optional; This block gets executed no matter if an exception is thrown or not. This block is generally used to release any resources that were opened in the method.

In our example, the try block contains scanner.nextInt() which throws an exception when the user does not input a number. In that scenario, an exception is thrown and the program of execution immediately jumps to the catch block and executes the statements in catch. In an exceptional scenario, the statements which are present after the statement which throws an exception are not executed. In the example above, the finally block releases the Scanner resource by closing it. If left unclosed then it is considered a resource leak. Although in our program the program exits immediately after the finally block. But in larger programs when the program does not exit after an exception and continues further, leaving resources hanging in memory will cause resource scarcity for other programs which want to use the same resources. To avoid such problems, you always should release the resource once it is of no use to you.

In the catch block above we catch the top level Exception. In this case since all exceptions have Exception class as its parent, every single Exception gets caught in the same catch block. However you can have multiple catch blocks each one catching the specific exception you want to catch.

In the above example what are the possible exceptions which could be thrown?
hint: Look into the javadocs for Scanner and in specific nextInt method

Exception InputMismatchException NoSuchElementException IllegalStateException Look into the javadocs of Scanner at https://docs.oracle.com/javase/9/docs/api/java/util/Scanner.html#nextInt--. You will notice that nextInt throws three types of exceptions as shown in the throws: section of the method doc.

What is the type of Exception thrown in the above example if you did not have a catch block?
hint: Remove the try,catch,finally block and see the stacktrace when you input a string instead of a number.

Exception InputMismatchException NoSuchElementException IllegalStateException

Let us now see how to add more catch blocks to the above program:

import java.util.Scanner;
import java.util.*;
public class ExceptionDemo {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        scanner.close();
        try {
            // statements which throw an exception are inside the try block
            System.out.println("Please input a number.");
            int a = scanner.nextInt();
            System.out.println("You input a number..Yay you followed instructions");
        } catch(InputMismatchException e) {
            // catch block contains statements which handle the exception
            System.out.println("Hey, you did not follow instructions! " +
            "You seem to have input something other than a pure number. " +
            "Please rerun the program by giving a valid number");

        } catch(NoSuchElementException e) {
             System.out.println("If input is exhausted this exception is thrown. " +
               "Check your program to see if you are reading input correctly");

        } catch(IllegalStateException e) {
              System.out.println("This exception is thrown in the scanner is closed " +
                "and you are trying to read the input from a closed scanner");

        } catch(Exception e) {
              System.out.println("This is a catch-all block as it catches the top level parent Exception class.  " +
               "This block should always come last");      

         } finally {
             // finally is optional. used to close any open resources. 
             // In our program, it is a good idea to close scanner object.
             scanner.close();
        }

    }
}

Rules for catch blocks

  • The catch block of a top level Exception should be after catch block of all its children.
  • Only one finally block is allowed but you can have any number of catch blocks.
  • When an exception is caught in any of the catch blocks, after executing the catch block, the program of execution jumps to the finally block if present. If there is no finally block then it jumps to the next statement after the try-catch statements.
  • Program of execution jumps to a catch block only when there is an exception thrown. In a normal execution without any exceptions, catch blocks are never executed. But finally block will be executed whether there is an exception or not.

throw statement

So far you have seen how exceptions are thrown and caught when something bad happens when a program is executing. In Java, we can also throw an exception from inside a program. You can create an Exception object by instantiating any of the available exception classes in Java or you can create your own exception class. The syntax for throw clause:

throw new MyNewException();

MyNewException is a class that you created by extending the top level java.lang.Exception class. You can also replace this by any of the ready made available exceptions.

When an exception is thrown, the normal execution is suspended and the compiler finds out a catch block that can handle the exception. If there is no catch block, then the exception is thrown out of the method and it lands in part of the program which invoked that method. Let us see an example:


import java.util.Scanner;
import javax.management.BadAttributeValueExpException;
public class ExceptionDemo {

    public static void main(String[] args) throws BadAttributeValueExpException {
        divideInputNumbers();
    }
    public static void divideInputNumbers() throws BadAttributeValueExpException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Input two numbers to divide");

        double dividend = scanner.nextDouble();
        double divisor = scanner.nextDouble();

        if(divisor == 0)
            throw new BadAttributeValueExpException("Cannot divide by zero");

        System.out.println("Answer:" + dividend/divisor);

    }
}

In the above example, you throw an exception by instantiating an object of the type BadAttributeValueExpException. Remember you could have created your own Exception or used any other exception in the Java library. We arbitrarily chose BadAttributeValueExpException which belongs to the package javax.management to show an example. Since this class does not belong to java.lang package, you have to have an import statement.

Since this is a checked exception and there is no try-catch block to catch the exception, you have to add the throws clause in the method signature. If you had chosen any of the RuntimeException types, then this requirement of adding the throws clause in the method signature does not exist. The method which throws the exception is invoked from the main method. Since the main method is not surrounding the method invocation by a try-catch clause, you have to add throws clause to the main method also.

Here is the same code with try-catch block added to the main method and notice that now you do not need to add the throws clause in the main method:


import java.util.Scanner;
import javax.management.BadAttributeValueExpException;
public class ExceptionDemo {

    public static void main(String[] args) {
        try {
            divideInputNumbers();
        } catch (BadAttributeValueExpException e) {
            System.out.println(e);
        }
    }
    public static void divideInputNumbers() throws BadAttributeValueExpException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Input two numbers to divide");

        int dividend = scanner.nextInt();
        int divisor = scanner.nextInt();

        if(divisor == 0)
            throw new BadAttributeValueExpException("Cannot divide by zero");

        System.out.println("Answer:" + dividend/divisor);

    }
}

Rules for exception propagation

  • The throws clause can be declared with any of the parent class of the exception being thrown. In the above example we could have replaced 'throws BadAttributeValueExpException' in the divideInputNumbers method signature with 'throws Exception' as Exception is the parent of all Exception types.
  • For all checked exceptions that are thrown, you have to either add throws clause to the method or surround the statements throwing the exception with a try-catch block.
  • When thrown, the invoking method should either surround the method throwing the exception with a try-catch block or should add throws clause to its method signature.

Reference: https://docs.oracle.com/javase/tutorial/essential/exceptions/

Exercise

Modify all the exercise programs that you have written thus far and add Exception blocks where ever appropriate.

results matching ""

    No results matching ""