1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: Custom exceptions let you create domain-specific error types that make your code more readable and maintainable. Extend Exception for checked exceptions (forced handling) or RuntimeException for unchecked (optional handling). Custom exceptions should have meaningful names and constructors.
Think of hospital error codes. Instead of generic "ERROR 500," you have "PatientNotFoundError," "InsuranceExpiredError"—specific, actionable, clear!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy: The Custom Warning Labels
- Built-in exceptions: Generic "Danger!" sign
- Custom exceptions: Specific "High Voltage—Electricians Only" sign
Custom exceptions provide context and clarity!
3. Technical Mastery (The "Deep Dive")
Creating Custom Exceptions
// Checked exception (extends Exception)
class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
// Unchecked exception (extends RuntimeException)
class InvalidAccountException extends RuntimeException {
public InvalidAccountException(String message) {
super(message);
}
}Best Practices
- Meaningful names: End with "Exception"
- Standard constructors: No-arg, message, message+cause
- Extend appropriate class: Exception vs RuntimeException
- Add fields if needed (error codes, timestamps)
4. Interactive & Applied Code
// Custom checked exception
class WithdrawalException extends Exception {
private double requestedAmount;
private double availableBalance;
public WithdrawalException(String message, double requested,double balance) {
super(message);
this.requestedAmount = requested;
this.availableBalance = balance;
}
public double getShortfall() {
return requestedAmount - availableBalance;
}
}
// Custom unchecked exception
class InvalidEmailException extends RuntimeException {
public InvalidEmailException(String email) {
super("Invalid email format: " + email);
}
}
class BankAccount {
private double balance;
private String email;
public BankAccount(double balance, String email) {
setEmail(email); // Uses custom exception
this.balance = balance;
}
public void withdraw(double amount) throws WithdrawalException {
if (amount > balance) {
throw new WithdrawalException(
"Insufficient funds",
amount,
balance
);
}
balance -= amount;
}
public void setEmail(String email) {
if (!email.contains("@")) {
throw new InvalidEmailException(email);
}
this.email = email;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(100, "user@example.com");
// Handle custom checked exception
try {
account.withdraw(150);
} catch (WithdrawalException e) {
System.out.println("❌ " + e.getMessage());
System.out.println("Shortfall: $" + e.getShortfall());
}
// Custom unchecked exception (optional handling)
try {
account.setEmail("invalid-email");
} catch (InvalidEmailException e) {
System.out.println("❌ " + e.getMessage());
}
}
}5. The Comparison & Decision Layer
| Scenario | Extend Exception (Checked) | Extend RuntimeException (Unchecked) |
|---|---|---|
| External factors | ✅ File not found, network down | |
| Validation errors | ✅ Invalid input format | |
| Business logic | ✅ Insufficient funds | |
| Programming bugs | ✅ Null parameter |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "When should you create a custom exception vs using built-in ones?" Answer: Create custom exceptions when:
- Domain-specific errors (not generic)
- Additional context needed (error codes, details)
- Different handling required than built-ins
- API clarity (self-documenting code)
Use built-ins for generic cases (IllegalArgumentException, IllegalStateException)
Pro-Tip: Add standard constructors:
class MyException extends Exception {
public MyException() { super(); }
public MyException(String msg) { super(msg); }
public MyException(String msg, Throwable cause) { super(msg, cause); }
public MyException(Throwable cause) { super(cause); }
}