Java Custom Exceptions

A Java custom exception is an exception class that you create for a specific problem in your program. It lets your code describe an error with a meaningful name, such as InvalidAgeException or EmptyNameException.

Custom exceptions are useful when built-in exceptions like IllegalArgumentException are too general for the rule you want to express.

Creating A Custom Exception

To create a checked custom exception, extend Exception. A checked exception must be handled with try-catch or declared with throws.

class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

public class Main {
    static void register(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("Age must be 18 or older.");
        }

        System.out.println("Registration accepted.");
    }

    public static void main(String[] args) {
        try {
            register(16);
        } catch (InvalidAgeException e) {
            System.out.println("Registration failed: " + e.getMessage());
        }
    }
}

Output:

Registration failed: Age must be 18 or older.

How It Works

The class InvalidAgeException extends Exception, so it becomes a real exception type. Its constructor accepts a message and passes it to the parent class with super(message).

The register() method declares throws InvalidAgeException because it might throw that checked exception. Inside the method, throw new InvalidAgeException(...) creates and throws the exception object.

In main(), the try-catch block handles the custom exception and prints its message with getMessage().

Checked Vs Unchecked Custom Exceptions

You choose the parent class based on how you want callers to handle the problem.

Parent class Type When to use
Exception Checked The caller should be forced to handle or declare the problem
RuntimeException Unchecked The problem usually means bad input, bad state, or a programming mistake

Unchecked Custom Exception

To create an unchecked custom exception, extend RuntimeException. The compiler does not require a throws declaration or a catch block, although you can still catch it when that makes sense.

class EmptyNameException extends RuntimeException {
    public EmptyNameException(String message) {
        super(message);
    }
}

public class Main {
    static void printBadge(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new EmptyNameException("Name cannot be blank.");
        }

        System.out.println("Badge: " + name);
    }

    public static void main(String[] args) {
        String[] names = {"Maya", ""};

        for (String name : names) {
            try {
                printBadge(name);
            } catch (EmptyNameException e) {
                System.out.println("Badge error: " + e.getMessage());
            }
        }
    }
}

Output:

Badge: Maya
Badge error: Name cannot be blank.

Good Habits

  • Name custom exceptions after the problem they represent, ending with Exception.
  • Include a constructor that accepts a clear message.
  • Use checked exceptions for problems the caller is expected to recover from.
  • Use unchecked exceptions for invalid arguments, invalid object state, or programmer mistakes.
  • Do not create a custom exception when a standard Java exception already describes the problem clearly.

Takeaway: custom exceptions give important errors a clear name and let your methods communicate exactly what went wrong.