Custom Exception in Java
Enhancing Error Management
Programmers can design unique exception types that are suited to the requirements of their application by using custom exceptions, commonly referred to as user-defined exceptions. Although Java has a large collection of built-in exceptions, custom exceptions are essential for managing mistakes specific to the logic or domain of your program.
Clarity and specificity are the main benefits of custom exceptions. They distinguish them from more general exceptions by enabling users of your library or API to precisely detect and manage issues from your code. Because it explains what went wrong and offers a methodical approach to fixing expected issues, this results in programs that are more reliable and maintainable.
Throwing Exceptions with the Keyword
To specifically raise an exception while a program is running, use the throw
keyword. The program’s usual flow instantly pauses when a throw
statement is invoked. Java then looks for a suitable catch
block to deal with that particular kind of exception. Until a suitable handler is discovered or the program ends because of the JVM’s default exception handler, the exception is propagated up the call stack to the caller method if no matching catch
block is found in the current method.
The syntax for using throw
is straightforward: throw ThrowableInstance;
An object of type Throwable
or one of its subclasses is required in this case for ThrowableInstance
to exist. Primitive types (such as int
or char
) and non-Throwable
classes (such as String
) cannot be thrown as exceptions. Usually, you use the new
operator to generate an exception object, which you then throw.
Example of throw:
public class ThrowDemo {
static void checkAge(int age) {
if (age < 18) {
// Manually throw an IllegalArgumentException
throw new IllegalArgumentException("Access denied - You must be at least 18 years old.");
} else {
System.out.println("Access granted - You are old enough!");
}
}
public static void main(String[] args) {
try {
checkAge(15); // This will cause an exception
} catch (IllegalArgumentException e) { // Catch the specific exception
System.out.println("Exception caught: " + e.getMessage());
}
System.out.println("Program continues after handling.");
}
}
Code Output:
Exception caught: Access denied - You must be at least 18 years old.
Program continues after handling.
Creating Custom Exception Classes
You usually extend the RuntimeException
class for an unchecked exception or the Exception
class for a verified exception in order to construct your own exception class.
- Checked Exceptions:
Exception
were checked and extended. The Java compiler makes sure that the method that may throw these exceptions either declares them (throw
keyword) or catches them (try-catch
). These pertain to “anticipated events that an application should be able to deal with” . - Unchecked Exceptions: Extend
RuntimeException
. The compiler does not enforce handling or declaring these. They are frequently the consequence of a bug and usually reflect “unanticipated events that an application cannot deal with”
Steps to create a custom exception class:
- Define the class: Extend
Exception
orRuntimeException
. - Add constructors: Provide constructors that often mirror those of the parent
Exception
class, typically including one that takes aString
message and callssuper(message)
. You can also add constructors that take aThrowable
cause
for chained exceptions - Override toString() (optional but recommended): This allows you to provide a custom string representation of your exception, which can be useful for logging or displaying messages.
Example: Custom Checked Exception
Let’s create a custom checked exception for an InsufficientFundsException
in a banking application:
// InsufficientFundsException.java
public class InsufficientFundsException extends Exception {
private double amountNeeded;
public InsufficientFundsException(double amountNeeded) {
super("Sorry, but you are short $" + amountNeeded);
this.amountNeeded = amountNeeded;
}
public double getAmountNeeded() {
return amountNeeded;
}
}
// CheckingAccount.java
class CheckingAccount {
private double balance;
private int accountNumber;
public CheckingAccount(int accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
double needs = amount - balance;
throw new InsufficientFundsException(needs); // Throwing custom exception
}
balance -= amount;
System.out.println("Withdrew $" + amount + ". New balance: $" + balance);
}
public double getBalance() {
return balance;
}
}
// BankDemo.java (Main class)
public class BankDemo {
public static void main(String[] args) {
CheckingAccount account = new CheckingAccount(101, 500.0);
try {
System.out.println("Current balance: $" + account.getBalance());
account.withdraw(200.0);
account.withdraw(400.0); // This will throw an InsufficientFundsException
} catch (InsufficientFundsException e) {
System.out.println("Caught exception: " + e.getMessage());
System.out.println("Amount needed for transaction: $" + e.getAmountNeeded());
} finally {
System.out.println("Transaction attempt finished. Final balance: $" + account.getBalance());
}
}
}
Code Output:
Current balance: $500.0
Withdrew $200.0. New balance: $300.0
Caught exception: Sorry, but you are short $100.0
Amount needed for transaction: $100.0
Transaction attempt finished. Final balance: $300.0