Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to create a custom exception

Short answer

Create a class that extends RuntimeException and pass the message to the parent constructor:

class InsufficientFundsException extends RuntimeException {
    InsufficientFundsException(String message) {
        super(message);
    }
}
 
void withdraw(int balance, int amount) {
    if (amount > balance) {
        throw new InsufficientFundsException("Need " + amount + ", have " + balance);
    }
}
 
void main() {
    try {
        withdraw(100, 250);
    } catch (InsufficientFundsException e) {
        IO.println("Error: " + e.getMessage());
    }
}

Output:

Error: Need 250, have 100

Checked or unchecked

A subclass of RuntimeException is unchecked: the compiler does not require handling it. A subclass of Exception is checked: the method must declare it in throws, and the caller must handle it:

class ConfigException extends Exception {
    ConfigException(String message) {
        super(message);
    }
}
 
String readSetting(String key) throws ConfigException {
    throw new ConfigException("Missing setting: " + key);
}
 
void main() {
    try {
        readSetting("db.url");
    } catch (ConfigException e) {
        IO.println(e.getMessage());
    }
}

Output:

Missing setting: db.url

The cause

If the exception happened because of another one, pass the original as the second argument — it is kept in getCause():

class ImportException extends RuntimeException {
    ImportException(String message, Throwable cause) {
        super(message, cause);
    }
}
 
void main() {
    try {
        try {
            Integer.parseInt("12x");
        } catch (NumberFormatException e) {
            throw new ImportException("Bad row 7", e);
        }
    } catch (ImportException e) {
        IO.println(e.getMessage());
        IO.println(e.getCause().getMessage());
    }
}

Output:

Bad row 7
For input string: "12x"

Extra data

An exception can hold fields — for example, an error code for an API response:

class ApiException extends RuntimeException {
    private final int status;
 
    ApiException(int status, String message) {
        super(message);
        this.status = status;
    }
 
    int status() {
        return status;
    }
}
 
void main() {
    try {
        throw new ApiException(404, "Order not found");
    } catch (ApiException e) {
        IO.println(e.status() + " " + e.getMessage());
    }
}

Output:

404 Order not found

Common mistake

Extending Exception but not declaring it in throws. The compiler refuses to build such code:

class ConfigException extends Exception {
    ConfigException(String message) {
        super(message);
    }
}
 
void load() {
    throw new ConfigException("no file");
}
 
void main() {
    load();
}

Output:

Main.java:8: error: unreported exception Main.ConfigException; must be caught or declared to be thrown

Functions used in this recipe

  • throw — Throws an exception and stops the method.
  • throws — Declares which checked exceptions a method may throw.
  • RuntimeException — The base class of unchecked exceptions — bugs in program logic.
  • Throwable.getMessage() — Returns the message text of an exception.

Thanks for reading!

Donate to the site — we work hard for you.