Exceptions

CSCI 1933 – Introduction to Algorithms and Data Structures
Adriana Picoral

Checked vs. un-checked exceptions

  • Checked: “Something outside your code went wrong” — file missing, network dropped, database unreachable. The compiler reminds you to have a plan.
  • Unchecked: “Your code has a bug” — you accessed a null object, went out of bounds, passed a bad argument. The fix is to write better code, not to catch the exception.

Checked Exception Handling

  • Checked exceptions must be checked!
  • Methods that throw checked exceptions must have a throws clause on signature
public void readFile(String path) throws IOException {
      FileReader file = new FileReader(path);  // throws checked IOException
}
  • But, not if the checked exception is caught
public void readFile(String path) {
      try {
          FileReader file = new FileReader(path);
      } catch (IOException e) {
          System.out.println("File not found: " + e.getMessage());
      }
  }

Unchecked Exceptions

The compiler does not require you to handle these. They typically represent bugs in your code — things that should not happen if the program is written correctly.

// No compiler warning -- blows up at runtime if array is length 3
  public void getElement(int[] arr) {
      System.out.println(arr[10]);  // ArrayIndexOutOfBoundsException
  }

Exception Hierarchy

Exceptions in Java are classes organized using inheritance, just like any other class hierarchy.

  Throwable
  ├── Error               (serious JVM problems -- don't catch these)
  │   ├── OutOfMemoryError
  │   └── StackOverflowError
  └── Exception                       (things programs should handle)
      ├── IOException                 (checked)
      ├── SQLException                (checked)
      └── RuntimeException            (unchecked)
          ├── NullPointerException
          ├── ArrayIndexOutOfBoundsException
          └── IllegalArgumentException

Exception Hierarchy

Because exceptions use inheritance, a catch block that catches a parent class will also catch all its subclasses:

  try {
      // ...
  } catch (RuntimeException e) {  // catches NullPointerException,
                                   // ArrayIndexOutOfBoundsException, etc.
  }

Throwing vs. Instantiating

An exception is just a regular Java object — it must be instantiated before it can be thrown. These are two separate steps:

// Instantiating only -- creates the object but nothing happens
new IllegalArgumentException("bad value");

// Throwing only -- won't compile, throw needs an exception object
throw;

// Instantiating AND throwing -- what you actually do
throw new IllegalArgumentException("bad value");

You can also separate the steps:

// Instantiate first
IllegalArgumentException ex = new IllegalArgumentException("bad value");

// Throw later
throw ex;

The throw keyword is what triggers the exception and unwinds the call stack — simply creating the object does nothing on its own.

Custom Exception class

You can build your own Exception class – meaningful name, carries useful data, can be caught specifically. Example:

// Thrown when a withdrawal exceeds the available balance
  public class InsufficientFundsException extends Exception {
      private double amount;
      private double balance;

      public InsufficientFundsException(double amount, double balance) {
          super("Cannot withdraw $" + amount + ". Current balance: $" + balance);
          this.amount = amount;
          this.balance = balance;
      }
  }

The finally Block

The finally block executes no matter what — whether an exception was thrown or not, whether it was caught or not.

  try {
      System.out.println("try");
      int result = 10 / 0;           // throws ArithmeticException
  } catch (ArithmeticException e) {
      System.out.println("catch");
  } finally {
      System.out.println("finally"); // ALWAYS runs
  }
  try
  catch
  finally

The finally Block

Even if the exception is not caught, finally still runs:

  try {
      System.out.println("try");
      int result = 10 / 0;
  } finally {
      System.out.println("finally"); // still runs before program crashes
  }
  try
  finally
  Exception in thread "main"...

Looking Back

What is the advantage to creating an entirely new Exception class (and throwing it) over just throwing an instance of an existing Exception with a custom “message?”