Creating custom exceptions

In Java, custom exceptions are user-defined exceptions that extend the built-in Exception or RuntimeException classes. They allow you to represent specific error conditions that are meaningful to your application domain, improving code clarity, maintainability, and robustness.


Why Create Custom Exceptions?

  1. Domain-Specific Clarity: Standard exceptions like IOException or NullPointerException may not describe application-specific issues precisely. Custom exceptions provide meaningful names like InsufficientFundsException or InvalidAccountNumberException.

  2. Improved Error Handling: They help in categorizing and handling errors based on your application’s logic rather than relying only on generic exceptions.

  3. Encapsulation of Additional Data: You can add extra fields to your custom exception classes to store useful information (e.g., error codes, context).


Steps to Create a Custom Exception

  1. Extend Exception or RuntimeException:

    • Use Exception if you want a checked exception.

    • Use RuntimeException for unchecked exceptions.

  2. Provide Constructors:

    • Default constructor.

    • Constructor with an error message.

    • Optional constructor with a cause (another exception).

  3. (Optional) Add Additional Fields:

    • To provide more context (like error codes).


1. Syntax for Checked Custom Exception

// Define the custom checked exception
class MyCheckedException extends Exception {
    
    // Default constructor
    public MyCheckedException() {
        super();
    }

    // Constructor with custom message
    public MyCheckedException(String message) {
        super(message);
    }

    // Constructor with message and cause
    public MyCheckedException(String message, Throwable cause) {
        super(message, cause);
    }
}Code language: JavaScript (javascript)

2. Syntax for Unchecked Custom Exception

// Define the custom unchecked exception
class MyUncheckedException extends RuntimeException {
    
    // Default constructor
    public MyUncheckedException() {
        super();
    }

    // Constructor with custom message
    public MyUncheckedException(String message) {
        super(message);
    }

    // Constructor with message and cause
    public MyUncheckedException(String message, Throwable cause) {
        super(message, cause);
    }
}Code language: JavaScript (javascript)

Program 1: Checked Custom Exception

Program Statement: Create a checked custom exception InvalidAgeException. If the age provided is below 18, throw the exception. Otherwise, print a welcome message.

// Custom checked exception
class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

public class CheckedExceptionDemo {
    public static void validateAge(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("Age must be 18 or above to vote.");
        } else {
            System.out.println("Welcome! You are eligible to vote.");
        }
    }

    public static void main(String[] args) {
        try {
            validateAge(16); // Change this to 20 to see valid output
        } catch (InvalidAgeException e) {
            System.out.println("Caught Exception: " + e.getMessage());
        }
    }
}
/*
Caught Exception: Age must be 18 or above to vote.
*/

Program 2: Unchecked Custom Exception

Program Statement: Create an unchecked custom exception EmptyStringException. If the string passed is null or empty, throw the exception. Otherwise, print the string in uppercase.

// Custom unchecked exception
class EmptyStringException extends RuntimeException {
    public EmptyStringException(String message) {
        super(message);
    }
}

public class UncheckedExceptionDemo {
    public static void printUpperCase(String text) {
        if (text == null || text.trim().isEmpty()) {
            throw new EmptyStringException("Input string cannot be null or empty.");
        } else {
            System.out.println("Uppercase: " + text.toUpperCase());
        }
    }

    public static void main(String[] args) {
        try {
            printUpperCase(""); // Change this to "hello" to see valid output
        } catch (EmptyStringException e) {
            System.out.println("Caught Exception: " + e.getMessage());
        }
    }
}
/*
Caught Exception: Input string cannot be null or empty.
*/

Custom Exceptions with Error Codes in Java

In real-world applications, especially in enterprise and distributed systems, it is often helpful to associate error codes with exceptions. This provides an easy way to identify and categorize different types of errors programmatically, enabling better error handling, logging, debugging, and even user feedback mechanisms.

Program Statement

Design a banking system where users can withdraw money from their account. If the user provides an invalid account number, tries to withdraw more money than the balance, or attempts an unauthorized operation, throw a custom exception with an appropriate error code and message.

// Custom exception class with error code
class BankingException extends Exception {
    private final int errorCode;

    public BankingException(String message, int errorCode) {
        super(message);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }
}

// Account class simulating a simple bank account
class Account {
    private String accountNumber;
    private double balance;
    private boolean isAuthorized;

    public Account(String accountNumber, double balance, boolean isAuthorized) {
        this.accountNumber = accountNumber;
        this.balance = balance;
        this.isAuthorized = isAuthorized;
    }

    public void withdraw(String inputAccountNumber, double amount) throws BankingException {
        if (!this.accountNumber.equals(inputAccountNumber)) {
            throw new BankingException("Invalid account number.", 1001);
        }

        if (!isAuthorized) {
            throw new BankingException("Unauthorized access attempt.", 1003);
        }

        if (amount > balance) {
            throw new BankingException("Insufficient balance.", 1002);
        }

        balance -= amount;
        System.out.println("Withdrawal successful. Remaining balance: $" + balance);
    }
}

// Main class to test the program
public class BankApp {
    public static void main(String[] args) {
        // Creating an authorized account with $500 balance
        Account acc = new Account("ABC123", 500.0, true);

        try {
            acc.withdraw("ABC123", 100.0); // Successful case
            acc.withdraw("XYZ999", 50.0);  // Invalid account number
        } catch (BankingException e) {
            System.out.println("Error Code: " + e.getErrorCode());
            System.out.println("Error Message: " + e.getMessage());
        }

        try {
            acc.withdraw("ABC123", 1000.0); // Insufficient balance
        } catch (BankingException e) {
            System.out.println("Error Code: " + e.getErrorCode());
            System.out.println("Error Message: " + e.getMessage());
        }

        // Simulate unauthorized user
        Account unauthorizedAcc = new Account("DEF456", 300.0, false);
        try {
            unauthorizedAcc.withdraw("DEF456", 50.0); // Unauthorized access
        } catch (BankingException e) {
            System.out.println("Error Code: " + e.getErrorCode());
            System.out.println("Error Message: " + e.getMessage());
        }
    }
}
/*
Withdrawal successful. Remaining balance: $400.0
Error Code: 1001
Error Message: Invalid account number.
Error Code: 1002
Error Message: Insufficient balance.
Error Code: 1003
Error Message: Unauthorized access attempt.
*/

Creating custom exceptions in Java empowers developers to define and handle error conditions in a more expressive and meaningful way. By extending the Exception class (for checked exceptions) or the RuntimeException class (for unchecked exceptions), developers can provide application-specific error messages and behaviors that align with business logic and program requirements.

Custom exceptions help:

  • Encapsulate domain-specific errors clearly, making the code more readable and maintainable.

  • Differentiate between types of exceptions, rather than relying on generic ones.

  • Encourage proper error handling by allowing developers to design their own exception hierarchy and flow control.

While creating custom exceptions, it’s important to:

  • Provide descriptive messages.

  • Use checked exceptions for recoverable conditions that the calling code should handle.

  • Use unchecked exceptions for programming errors or when the client code is not expected to recover.

Ultimately, well-designed custom exceptions contribute to robust, maintainable, and self-documenting code.

Scroll to Top