Skip to content
Theory · Exceptions · lesson 8 of 12

throw and your own exceptions

In the lesson on checked exceptions, the method load threw an exception itself for the first time, with the throw statement. In the building, this is the fire alarm button on the wall: anyone who notices smoke can press it without waiting for a sensor. Let's see when to press it yourself and how to make an alarm with your own label.

Say the method withdraw takes money from an account. What should it do when asked for more than there is? It could return -1, but the caller may forget to check that and print -1 as the balance. An exception cannot be forgotten: if no one answers it, the program stops.

Your own exception class

Java has no ready class for "not enough money", but you can write one. An exception is an ordinary class, a subclass of Exception:

JAVA
public class Main {
    public static void main(String[] args) {
        var error = new InsufficientFundsException("need 50 more");
        System.out.println(error.getMessage()); // need 50 more
    }
}

class InsufficientFundsException extends Exception {
    InsufficientFundsException(String message) {
        super(message);
    }
}

Let's take it apart:

  1. class InsufficientFundsException extends Exception is a new exception class. Its parent is Exception, so the exception is checked. By tradition the name ends in Exception.
  2. The constructor takes a message and passes it to the parent through super(message).
  3. The method getMessage() is declared in the class Throwable, and our class inherits it. It returns the message that was passed to the constructor.
  4. In main the object is created with new, like any other. Nothing happens: until an exception is thrown, it is an ordinary object.

If the class inherits from RuntimeException, the exception is unchecked. A checked one is chosen for a situation the caller must be ready for, like a lack of money. An unchecked one is for an error in the caller's code.

Throwing your own exception

Now withdraw can press the alarm button itself:

JAVA
static int withdraw(int balance, int amount)
        throws InsufficientFundsException {
    if (amount <= 0) {
        throw new IllegalArgumentException("amount must be positive");
    }
    if (amount > balance) {
        int lack = amount - balance;
        throw new InsufficientFundsException("need " + lack + " more");
    }
    return balance - amount;
}

The method checks for two different troubles:

  1. An amount of zero or less is an error in the code of whoever called the method. Java has a ready unchecked class for it, IllegalArgumentException, "a wrong argument". The NumberFormatException that parseInt throws is its subclass.
  2. Not enough money is an ordinary real-life situation, and the caller must be ready for it. Our checked exception reports it, so the header says throws InsufficientFundsException.

throw ends the method at once, like an exception from the JVM: return is never reached. A call to withdraw is now written inside try:

JAVA
try {
    System.out.println(withdraw(100, 30)); // 70
    System.out.println(withdraw(100, 150));
} catch (InsufficientFundsException e) {
    System.out.println("Refused: " + e.getMessage()); // Refused: need 50 more
}

Let's go line by line:

  1. withdraw(100, 30) returns 70, and the program prints it.
  2. withdraw(100, 150) throws an exception with the message need 50 more, and the second println does not run.
  3. catch catches it and prints Refused: need 50 more.

And a call withdraw(100, -5) in the same try would throw an IllegalArgumentException. This catch does not catch it, because the class is different, and the program stops:

TEXT
Exception in thread "main" java.lang.IllegalArgumentException: amount must be positive

That is the intent: an error in the code is not hidden but fixed.

throw and throws

These two words get mixed up all the time, and interviewers often ask how they differ:

WordWhere it goesWhat it does
throwin the method bodythrows one exception object now
throwsin the method header, after the parenthesesdeclares which exceptions may leave the method

throw is an action: pressing the alarm button. throws is the "we pass it upstairs" sign on the floor. For a checked exception the sign is required, for an unchecked one it is not.

Here the button picture is imprecise. A real siren sounds the same whoever switched it on. An exception carries its own class and message, and by the class catch decides whether this alarm is its own.

Common mistake

In your own exception class you forgot to pass the message to the parent:

JAVA
class InsufficientFundsException extends Exception {
    InsufficientFundsException(String message) { // ✗
    }
}

The program compiles, but instead of Refused: need 50 more it prints Refused: null. The constructor did not call super(message), so the compiler inserted super() with no arguments. The parent never learned the message, and getMessage() returns null. Pass the message to the parent: write super(message); as the constructor's first line.

Key points

  • throw new … throws an exception at once and ends the method; a wrong argument has the ready IllegalArgumentException.
  • Your own exception is a subclass of Exception (checked) or RuntimeException (unchecked) that passes its message through super(message).
  • getMessage() returns the message passed to the constructor.
  • throw throws an exception in the method body, and throws declares it in the header.