Software Engineering for Self-Directed Learners »

C++ to Java →

Exceptions

What are Exceptions?

Can explain Java Exceptions

Given below is an extract from the -- Java Tutorial, with some adaptations.

There are three basic categories of exceptions In Java:

  • Checked exceptions: exceptional conditions that a well-written application should anticipate and recover from. All exceptions are checked exceptions, except for Error, RuntimeException, and their subclasses.

Suppose an application prompts a user for an input file name, then opens the file by passing the name to the constructor for java.io.FileReader. Normally, the user provides the name of an existing, readable file, so the construction of the FileReader object succeeds, and the execution of the application proceeds normally. But sometimes the user supplies the name of a nonexistent file, and the constructor throws java.io.FileNotFoundException. A well-written program will catch this exception and notify the user of the mistake, possibly prompting for a corrected file name.

  • Errors: exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from. Errors are those exceptions indicated by Error and its subclasses.

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.

  • Runtime exceptions: conditions that are internal to the application, and that the application usually cannot anticipate or recover from. Runtime exceptions are those indicated by RuntimeException and its subclasses. These usually indicate programming bugs, such as logic errors or improper use of an API.

Consider the application described previously that passes a file name to the constructor for FileReader. If a logic error causes a null to be passed to the constructor, the constructor will throw NullPointerException. The application can catch this exception, but it probably makes more sense to eliminate the bug that caused the exception to occur.

Errors and runtime exceptions are collectively known as unchecked exceptions.


How to use Exceptions

Can use Java Exceptions

The content below uses extracts from the -- Java Tutorial, with some adaptations.

A program can catch exceptions by using a combination of the try, catch blocks.

  • The try block identifies a block of code in which an exception can occur.
  • The catch block identifies a block of code, known as an exception handler, that can handle a particular type of exception.

The writeList() method below calls a method process() that can cause two type of exceptions. It uses a try-catch construct to deal with each exception.

public void writeList() {
    print("starting method");
    try {
        print("starting process");
        process();
        print("finishing process");

    } catch (IndexOutOfBoundsException e) {
        print("caught IOOBE");

    } catch (IOException e) {
        print("caught IOE");

    }
    print("finishing method");
}

Some possible outputs:

No exceptions IOException IndexOutOfBoundsException
starting method
starting process
finishing process
finishing method
starting method
starting process
finishing process
caught IOE
finishing method
starting method
starting process
finishing process
caught IOOBE
finishing method

You can use a finally block to specify code that is guaranteed to execute with or without the exception. This is the right place to close files, recover resources, and otherwise clean up after the code enclosed in the try block.

The writeList() method below has a finally block:

public void writeList() {
    print("starting method");
    try {
        print("starting process");
        process();
        print("finishing process");

    } catch (IndexOutOfBoundsException e) {
        print("caught IOOBE");

    } catch (IOException e) {
        print("caught IOE");

    } finally {
        // clean up
        print("cleaning up");
    }
    print("finishing method");
}

Some possible outputs:

No exceptions IOException IndexOutOfBoundsException
starting method
starting process
finishing process
cleaning up
finishing method
starting method
starting process
finishing process
caught IOE
cleaning up
finishing method
starting method
starting process
finishing process
caught IOOBE
cleaning up
finishing method
  • The try statement should contain at least one catch block or a finally block and may have multiple catch blocks.

  • The class of the exception object indicates the type of exception thrown. The exception object can contain further information about the error, including an error message.

You can use the throw statement to throw an exception. The throw statement requires a object as the argument.

Here's an example of a throw statement.

if (size == 0) {
    throw new EmptyStackException();
}

In Java, Checked exceptions are subject to the Catch or Specify Requirement: code that might throw checked exceptions must be enclosed by either of the following:

  • A try statement that catches the exception. The try must provide a handler for the exception.
  • A method that specifies that it can throw the exception. The method must provide a throws clause that lists the exception.

Unchecked exceptions are not required to follow to the Catch or Specify Requirement but you can apply the requirement to them too.

Here's an example of a method specifying that it throws certain checked exceptions:

public void writeList() throws IOException, IndexOutOfBoundsException {
    print("starting method");
    process();
    print("finishing method");
}
Some possible outputs:
No exceptions IOException IndexOutOfBoundsException
starting method
finishing method
starting method
finishing method
starting method
finishing method

Java comes with a collection of built-in exception classes that you can use. When they are not enough, it is possible to create your own exception classes.

[Key Exercise] parse rectangle descriptor

The Main class below parses a string descriptor of a rectangle of the format "WIDTHxHEIGHT" e.g., "3x4" and prints the area of the rectangle.

public class Main {

    public static void printArea(String descriptor){
        //TODO: modify the code below
        System.out.println(descriptor + "=" + calculateArea(descriptor));
    }

    private static int calculateArea(String descriptor) {
        //TODO: modify the code below
        String[] dimensions = descriptor.split("x");
        return Integer.parseInt(dimensions[0]) * Integer.parseInt(dimensions[1]);
    }

    public static void main(String[] args) {
        printArea("3x4");
        printArea("5x5");
    }
}

3x4=12
5x5=25
  1. Update the code of printArea to print an error message if WIDTH and/or HEIGHT are not numbers e.g., "Ax4"
    calculateArea will throw the unchecked exception NumberFormatException if the code tries to parse a non-number to an integer.

  2. Update the code of printArea to print an error message if the descriptor is missing WIDTH and/or HEIGHT e.g., "x4"
    calculateArea will throw the unchecked exception IndexOutOfBoundsException if one or both dimensions are missing.

  3. Update the code of calculateArea to throw the checked exception IllegalShapeException if there are more than 2 dimensions e.g., "5x4x3" and update the printArea to print an error message for those cases. Here is the code for the IllegalShapeException.java

public class IllegalShapeException extends Exception {
  //no other code needed
}

Here is the expected behavior after you have done the above changes:

public class Main {

    //...

    public static void main(String[] args) {
        printArea("3x4");
        printArea("3xy");
        printArea("3x");
        printArea("3");
        printArea("3x4x5");
    }
}

3x4=12
WIDTH or HEIGHT is not a number: 3xy
WIDTH or HEIGHT is missing: 3x
WIDTH or HEIGHT is missing: 3
Too many dimensions: 3x4x5

Partial solution