Skip to content

Programming by Design

If you're not prepared to be wrong, you'll never come up with anything original. – Sir Ken Robinson

  • About
  • Java-PbD
  • C-PbD
  • ASM-PbD
  • Algorithms
  • Other

Chapter 12 – Exceptions

Posted on June 2, 2019January 12, 2025 By William Jojo
Java Book

(Updated January 12, 2025)

Table of contents

    Overview
    Handling Exceptions
    The try/catch/finally Block
    The Exception Hierarchy
    Checked and Unchecked Exceptions
    Creating an Exception Class
    Exercises

Overview

When we develop a Java program, there are many opportunities to fix syntax and logic problems. Some possibilities are available through error messages from the compiler, which refuses to allow us to proceed until the syntax errors are corrected. Other options are from our testing; when logic errors are identified and repaired due to viewing results, we know them to be incorrect.

The problem of runtime errors, or exceptions, is difficult to perceive at first if you have not given them much thought or dismissed out of hand the occasional InputMismatchException or NumberFormatException. If these have troubled you, there is a simple mechanism by which we can control the exceptions instead of the exceptions causing our program to halt with a stack trace abruptly.


Handling Exceptions

The use of types in programming will eventually lead to an issue of compatibility. It is easy to visualize an integer becoming a floating-point number – add a period and a zero at the end of the integer, and you now have a floating-point quantity. The compiler will allow an int to be assigned to a float or double type. Ask the compiler to look the other way when assigning a double to an int variable, and your program will never get off the ground until you fix the “possible loss of precision” error that the compiler has identified. There are two choices:

  1. Make the double an int permanently by changing variable types as needed and making them all the same type.
  2. Use a cast to temporarily alter the type and acknowledge that we realize a potential for a calculation error and accept the consequences.

This method of control over our program logic and data works very well. However, what happens when we use the nextInt() method of the Scanner class and the data file or user provides a double quantity? We would expect a InputMismatchException to occur. The same would be true if the user entered “xyz” when our program was calling nextDouble().

We need a way to control the program and catch these exceptions before they terminate the program. This is necessary for several reasons:

  • The program may recover from the error and retry the action.
  • The program can detect the error and perform a controlled shutdown rather than an abrupt halt.
  • The program may be easier to manage and will be written more professionally.

The try/catch/finally Block

When an exception occurs, Java creates an object of the specific exception class. Some of these classes are the class NumberFormatException and the class InputMismatchException. All exception classes are subclasses of the superclass Exception.

We can surround a set of statements that we know could generate an exception with the try keyword, then catch exceptions and perform work based on the type of exception that has occurred. We can also use the finally block to cause work to be done whether an exception occurred.

Consider the following:

Scanner kb = new Scanner(System.in);
int num;

try {
    System.out.print("Enter a number: ");
    num = kb.nextInt();
} catch (InputMismatchException imeRef) {
    System.out.println("Your input was not an integer: " + imeRef.toString());
}

This code block enables us to try reading an integer from the user and then catch an exception should it occur. Note that the InputMismatchException object is referenced with the imeRef parameter. Let us inspect another piece of code:

Scanner kb = new Scanner(System.in);
int line=0, val;
String s;

while (kb.hasNext()) {
    s = kb.next();
    line++;
    try {
        val = Integer.parseInt(s, 16);
    } catch (Exception eRef) {
        System.out.println("Possible data corruption at line " + line);
        System.exit(1);
    } finally {
        System.out.println("The data was \"" + s + "\"");
    }
}

This example uses the superclass Exception object with the eRef reference parameter. The finally block is used here as a debug tool. Recall that the finally block will be executed regardless of the presence of an exception.

It is essential to realize that the try block ceases execution of statements as soon as an exception occurs, and the catch blocks are only executed when an exception occurs. Never place the Exception superclass catch above any other Exception subclasses; otherwise, the superclass Exception will always be applied. In other words, the order of your catch blocks is essential.


The Exception Hierarchy

The Exception class is a subclass of the Throwable class, which is a subclass of the Object class. Table 1 shows some of the methods of the Throwable class and Table 2 shows the constructors for the Exception class. Since the class Exception is a subclass of Throwable, it inherits the Throwable methods.

Throwable Class Constructors and Methods
public Throwable()
Default constructor. Creates a Throwable object with no message string.
public Throwable(String msg)
Creates a Throwable object with the message string msg.
public String getMessage()
Returns the message stored in the Throwable object.
public void printStackTrace()
Print the stack trace to view the sequence of method calls.
public void printStackTrace(PrintWriter stream)
Print the stack trace to view the sequence of method calls to the stream named stream.
public String toString()
Return string representation of the Throwable object.

Table 1: Constructors and Methods of the the Throwable class.

Exception Class Constructors
public Exception()
Default constructor. Creates an Exception object.
public Exception(String msg)
Creates an Exception object with the message string msg.

Table 2: Constructors of the Exception class.

The exception hierarchy is relatively simple to follow. Everything is ultimately a subclass of Exception. There are many subclasses from ClassNotFoundException to the RunTimeException and IOException.

The RuntimeException is a rather large subset of exceptions that can only occur while your program runs. As a result, you will see many subclasses listed under the RuntimeException.

The Exception class is defined in the java.lang package. Other subclasses of Exception are also defined in java.lang as well as java.util, java.io and java.awt to name a few.

Some exception classes defined in java.lang:

  • Exception
    • ClassNotFoundException
    • CloneNotSupportedException
    • IllegalAccessException
    • InstantiationException
    • InterruptedException
    • NoSuchFieldException
    • NoSuchMethodException
    • RuntimeException
      • ArithmeticException
      • ClassCastException
      • IllegalArgumentException
        • NumberFormatException
      • IndexOutOfBoundsException
        • ArrayIndexOutOfBoundsException
        • StringIndexOutOfBoundsException
      • NullPointerException

Some exception classes defined in java.util:

  • Exception
    • RuntimeException
      • EmptyStackException
      • NoSuchElementException
        • InputMismatchException

Some exception classes defined in java.io:

  • Exception
    • IOException
      • EOFException
      • FileNotFoundException

The online Java documentation at the Oracle site is the best place to determine what exceptions a method or constructor will throw.

As a final note about handling multiple exceptions, you could always catch the superclass Exception and then process an if/else-if/else-if/else against the superclass reference parameter like so:

try {
    // sequence of input and
    // arithmetic statements that could
    // produce Scanner or division by zero
    // errors.
} catch (Exception eRef) {
    if ( eRef instanceof ArithmeticException)
        // statements
    else if (eRef instanceof InputMismatchException)
        // statements
    else
        // statements
}

Checked and Unchecked Exceptions

Java has broken down the predefined exceptions into two categories: checked and unchecked. Checked exceptions are made by the compiler, meaning an exception that the compiler can successfully identify.

The FileNotFoundException is a prime example, as is the IOException. Recall that this exception must be thrown often by the main() method of your program. This results in a throws FileNotFoundException at the end of the main() declaration for any program that uses FileReader or PrintWriter. Of course, we ignore the exception since we have not set up a try/catch block to properly deal with the potential problem.

Unchecked exceptions are those that you may choose to catch or not. These are also exceptions that the compiler cannot accurately detect. These types of exceptions include InputMismatchException, NumberFormatException and ArithmeticException. Essentially, all of the RuntimeException subclasses are unchecked and should be checked by you, the programmer, if you wish to make your programs more reliable and achieve reasonable recoverability.

Remember that you may ignore, throw, or re-throw a caught exception when writing your methods. You need to throw an instantiated exception object to throw or re-throw an exception. For example:

throw new Exception("Text associated with the exception");

Or with:

throw new ArithmeticException("bad math skills!");

Or, in the case of rethrowing:

try {
    // statements
}
catch (InputMismatchException imeRef) {
    // statements
    throw imeRef;
}

Creating an Exception Class

We will build a program that creates an exception class for improperly formatted data. This program requires input containing at least two fields in a string separated by a colon.

We will start with the class that defines a DataFormatExeption. This exception extends the Exception class and defines two constructors similar to Exception. The constructors call the superclass constructor super(). This class is shown in Example 1.

DataFormatException.java
public class DataFormatException extends Exception {
    public DataFormatException () {
        super("Data must contain at least one colon");
    }

    public DataFormatException(String msg) {
        super(msg);
    }
}

Example 1: Code defining the DataFormatException class.

The class that will exercise our new DataFormatException will also demonstrate how to catch and recover from the exception. In this case, we are showing that you do not need to stop execution just because something terrible has occurred. (Although sometimes that is all that is left to you!)

ExceptTest.java
import java.util.Scanner;

class DataFormatException extends Exception {
    public DataFormatException() {
        super("Data must contain at least one colon");
    }

    public DataFormatException(String msg) {
        super(msg);
    }
}

public class ExceptTest {

    static Scanner kb = new Scanner(System.in);

    public static void main(String[] args) {

        String s;
        int pos;
        System.out.println("\nEnter data with at least one colon:");

        while (kb.hasNext()) {
            try {
                s = kb.nextLine();
                pos = s.indexOf(":");
                if (pos == -1)
                    throw new DataFormatException();
                else
                    System.out.println("The value entered is " + s);
            } catch (DataFormatException dfeRef) {
                System.out.println(dfeRef);
            } catch (Exception eRef) {
                System.out.println(eRef);
            }
            System.out.println("\nEnter data with at least one colon:");
        }
    }
}

Example 2: Program to exercise the DataFormatException class.

The program ExceptTest in Example 2 uses an EOF-based while loop. You could, of course, use a do-while. Consider how your program needs to be constructed to facilitate moving forward in the event of an exception.

This program also throws the exception within the try block, which is subsequently caught in the catch block. This demonstrates the tight level of control that the try/catch block offers a programmer.

Notice how the while loop contains the try/catch block and how there is no call to System.exit() as a result of the exception occurring. Remember that our goal is to keep the program going. Any data entry without a colon will result in the exception being displayed.


Exercises

  1. (Beginner) Create a custom exception and write some interactive code around it. See the example above and come up with an idea.
  2. (Intermediate) Create an exception to throw after catching some other exceptions. See the list for ideas.
  3. (Intermediate) Write a Java program that reads a list of integers from the user and throws an exception if any numbers are duplicates.
  4. (Intermediate) Write a Java program to create a method that reads a file into a variable and throws an exception if the file is not found or another error is encountered.
  5. (Advanced) Write a program to create a basic stack (it can be an array for simplicity). Create exceptions for StackOverflow and StackUnderflow at a minimum.

Post navigation

❮ Previous Post: Chapter 11 – Inheritance and Polymorphism
Next Post: Chapter 13 – Recursion ❯

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Copyright © 2018 – 2025 Programming by Design.