Java Exception Handling Cheat Sheet

Overview

While preparing for the OCP 17 exam, I created some notes to aid my learning. This is the first of many posts in this series.

I hope you find it helpful.

Java Exception Handling Cheat Sheet

The Throwable family

The Throwable family in Java
The Throwable family in Java
  • Throwable
    • Error
    • Exception
      • RuntimeException
      • IOException
      • SQLException

Checked vs. unchecked exceptions

  • Checked exceptions
    • Must be handled or declared
    • Usually indicates a problem outside of the program
    • Subclasses of Exception that are not RuntimeException
  • Unchecked exceptions
    • Do not have to be handled or declared
    • Usually indicates a problem inside the program
    • Subclasses of RuntimeException or Error

Overriding methods with declared exceptions

  • The exception declared in an overriding method cannot be more general than the exception declared in the overridden method
  • The overriding method can declare no exceptions even if the overridden method declares exceptions

Example

import java.io.IOException;//This will compile
class A {
    void foo() throws IOException {}
}

class B extends A {
    void foo() throws FileNotFoundException {}
}

// However, this will not compile since FileNotFoundException is a subclass of IOException
class A {
    void foo() throws FileNotFoundException {}
}

class B extends A {
    void foo() throws IOException {}
}
  • If the overridden method does not declare any exceptions, the overriding method cannot declare any checked exceptions
class A {
    void foo() {}
}

class B extends A {
    void foo() throws IOException {} // This will not compile
}
  • If the overridden method declares a checked exception but the overriding method doesn’t throw any, it’s not necessary to handle the exception in the overriding method
    class A {
        public void method() throws Exception {
            throw new Exception("Hello World");
        }
    }

    class B extends A {
        @Override
        public void method() {
            throw new RuntimeException("Hello World");
        }
    }

    public void test() {
        A a = new B();
//        a.method(); //## This will not compile

        B b = new B();
        b.method(); // ## This will compile
    }

RuntimeException

  • RuntimeException is the superclass of all exceptions that can be thrown during the normal operation of the Java Virtual Machine
  • A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught

  • Common subclasses of RuntimeException
  • ArithmeticException
    • Thrown when an exceptional arithmetic condition has occurred. For example, an integer “divide by zero” throws an instance of this class
  • ArrayIndexOutOfBoundsException
    • Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
  • IllegalArgumentException
  • NullPointerException
  • NumberFormatException
  • SecurityException
  • ClassCastException
    • Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. For example, the following code generates a ClassCastException:
//This will throw an ClassCastException
Object invalidInteger = "Hello World";
Integer x = (Integer) invalidInteger;

Checked exceptions

  • Checked exceptions are the exceptions that are checked at compile time
  • If some code within a method throws a checked exception, then the method must either handle the exception or it must specify the exception using the throws keyword
  • Examples
    • FileNotFoundException
    • IOException
    • ClassNotFoundException
    • CloneNotSupportedException
    • SQLException
    • NotSerializableException

Error

  • An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch
  • Most such errors are abnormal conditions
  • Example
    • ExceptionInInitializerError
    • StackOverflowError
    • NoClassDefFoundError
    • OutOfMemoryError
    • AssertionError

Exception handling

  • The try block contains a set of statements where an exception can occur
  • A try block is always followed by a catch block, which handles the exception that occurs in the associated try block
  • A try block must be followed by catch blocks or finally block or both
  • It is not possible to have a catch or finally block without a try block
  • Example
public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            int c = a / b; // ArithmeticException
            System.out.println(c);
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException occurred");
        }
    }
}

Chaining catch blocks

  • A try block can be followed by multiple catch blocks
  • The catch block that gets executed is the one that corresponds to the exception thrown by the try block
  • Example
public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            int c = a / b; // ArithmeticException
            System.out.println(c);
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException occurred");
        } catch (Exception e) {
            System.out.println("Exception occurred");
        }
    }
}

In this example, only the first catch block will be executed since the exception thrown by the try block is ArithmeticException

  • When chaining catch blocks, the catch block for the subclass must come before the catch block for the superclass
  • If one of the catch blocks is not reachable, the compiler will complain

Multicatch block

  • A multicatch block is a catch block that handles more than one type of exception
  • Example
public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            int c = a / b; // ArithmeticException
            System.out.println(c);
        } catch (ArithmeticException | NullPointerException e) {
            System.out.println("ArithmeticException or NullPointerException occurred");
        } catch (Exception e) {
            System.out.println("Exception occurred");
        }
    }
}

The exceptions in a multicatch block must not be in the same inheritance hierarchy (cannot have a parent/child relationship)

The finally block

  • The finally block always executes when the try block exits
  • This ensures that the finally block is executed even if an unexpected exception occurs
  • But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break
  • Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated
  • When having the finally block, the catch block is optional
  • If there are return statements in the try block, the catch block and the finally block, the return statement in the finally block will be executed just before the method returns
  • The finally block will not be executed if the JVM exits while the try or catch block is being executed (e.g. System.exit() is called)

== Try with resources

  • The try-with-resources statement is a try statement that declares one or more resources
  • A resource is an object that must be closed after the program is finished with it
  • The try-with-resources statement ensures that each resource is closed at the end of the statement
  • Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource
  • The resources declared in the try block are effectively final, you cannot reassign them
  • You can declare the resources outside of the try block, but you cannot declare them again in the try block
  • Example
public class TryWithResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("IOException occurred");
        }
    }
}

Behind the scene, try with resources is translated to the following code

public class TryWithResourcesExample {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("test.txt"));
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("IOException occurred");
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    System.out.println("IOException occurred");
                }
            }
        }
    }
}
  • As you can see, there is an implicit finally block that closes the resource
  • As a programmer, you still can declare your finally block, however the implicit finally block will be executed first
  • When writing try with resources, the resources are closed in the reverse order of their creation
  • The catch block is optional when using try with resources
  • You can declare multiple resources in the try with resources statement, the resources are separated by semicolons
  • Example
public class TryWithResourcesExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("test2.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
            }
        } catch (IOException e) {
            System.out.println("IOException occurred");
        }
    }
}

The resources declared are scoped to the try block, you cannot access the resources outside of the try block (e.g. in the catch block or the finally block)

Order of execution in try with resources

  • Consider the following code block
class SomeExamples {
    static class MyResource implements AutoCloseable {
        private String resourceName;
        public MyResource(String resourceName) {
            this.resourceName = resourceName;
        }
        @Override
        public void close() throws Exception {
            System.out.println("Closing " + resourceName);
        }
    }

    public static void main(String[] args) {
        try (MyResource myResource1 = new MyResource("First");
             MyResource myresource2 = new MyResource("Second")) {
            System.out.println("In Try Block");
            throw new RuntimeException();
        } catch (Exception e) {
            System.out.println("In Catch Block");
        } finally {
            System.out.println("In Finally Block");
        }
    }

    //The order of output when running main:
    //In Try Block
    //Closing Second
    //Closing First
    //In Catch Block
    //In Finally Block

}

Throwing exception in resource (Suppressed Exception)

  • If an exception is thrown in the try block and another exception is thrown while closing the resources, the exception thrown while closing the resources will be suppressed
  • The exception in the try block is called the primary exception, the exception thrown while closing the resources is called the suppressed exception
class SuppressedExceptionDemo {

    static class MyProblematicResource implements AutoCloseable {
        private String resourceName;
        public MyProblematicResource(String resourceName) {
            this.resourceName = resourceName;
        }
        @Override
        public void close() throws Exception {
            System.out.println("Closing " + resourceName);
            throw new Exception("Exception in close method");
        }
    }

    public static void main(String[] args) {
        try (var resource = new MyProblematicResource("First")) {
            System.out.println("In Try Block");
            throw new RuntimeException("Exception in try block");
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println("Exception in catch block");
            for (Throwable t : e.getSuppressed()) {
                System.out.println(t.getMessage());
            }
        } finally {
            System.out.println("In Finally Block");
        }
    }
}
/*
- Executing the code above will produce the following output


In Try Block
Closing First
Exception in try block
Exception in catch block
Exception in close method
In Finally Block

*/

Conclusion

There you have it, the complete cheat sheet of Java exception handling. For further material, checkout my Github repo for this series here.

Leave a Comment