Introduction
Custom exceptions allow you to create your own exception types for specific error conditions.
Why Create Custom Exceptions?
- Specific errors for your application
- Better error handling
- Meaningful names
- Additional information
Basic Syntax
// Checked exception
class MyException extends Exception {
MyException(String message) {
super(message);
}
}
// Unchecked exception
class MyRuntimeException extends RuntimeException {
MyRuntimeException(String message) {
super(message);
}
}
Simple Custom Exception
// Custom checked exception
class InvalidAgeException extends Exception {
InvalidAgeException(String message) {
super(message);
}
}
class Person {
int age;
void setAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
throw new InvalidAgeException("Age must be between 0 and 150");
}
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person();
try {
p.setAge(200);
} catch (InvalidAgeException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Checked vs Unchecked Custom Exception
Checked (extends Exception):
class InsufficientFundsException extends Exception {
InsufficientFundsException(String message) {
super(message);
}
}
// Must declare with throws
void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Not enough funds");
}
balance -= amount;
}
Unchecked (extends RuntimeException):
class InvalidAmountException extends RuntimeException {
InvalidAmountException(String message) {
super(message);
}
}
// No need to declare
void deposit(double amount) {
if (amount <= 0) {
throw new InvalidAmountException("Amount must be positive");
}
balance += amount;
}
With Additional Information
class AccountException extends Exception {
private String accountNumber;
private double attemptedAmount;
AccountException(String message, String accountNumber, double attemptedAmount) {
super(message);
this.accountNumber = accountNumber;
this.attemptedAmount = attemptedAmount;
}
String getAccountNumber() {
return accountNumber;
}
double getAttemptedAmount() {
return attemptedAmount;
}
}
class BankAccount {
String accountNumber;
double balance;
void withdraw(double amount) throws AccountException {
if (amount > balance) {
throw new AccountException(
"Insufficient funds",
accountNumber,
amount
);
}
balance -= amount;
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.accountNumber = "ACC001";
account.balance = 1000;
try {
account.withdraw(1500);
} catch (AccountException e) {
System.out.println(e.getMessage());
System.out.println("Account: " + e.getAccountNumber());
System.out.println("Attempted: $" + e.getAttemptedAmount());
}
}
}
Multiple Constructors
class CustomException extends Exception {
// No-arg constructor
CustomException() {
super("Custom exception occurred");
}
// Constructor with message
CustomException(String message) {
super(message);
}
// Constructor with message and cause
CustomException(String message, Throwable cause) {
super(message, cause);
}
}
Complete Example: Student System
// Custom exceptions
class InvalidMarksException extends Exception {
InvalidMarksException(String message) {
super(message);
}
}
class InvalidRollNumberException extends Exception {
InvalidRollNumberException(String message) {
super(message);
}
}
class Student {
private int rollNumber;
private String name;
private int marks;
void setRollNumber(int rollNumber) throws InvalidRollNumberException {
if (rollNumber <= 0) {
throw new InvalidRollNumberException("Roll number must be positive");
}
this.rollNumber = rollNumber;
}
void setMarks(int marks) throws InvalidMarksException {
if (marks < 0 || marks > 100) {
throw new InvalidMarksException("Marks must be between 0 and 100");
}
this.marks = marks;
}
void setName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
this.name = name;
}
void display() {
System.out.println("Roll: " + rollNumber);
System.out.println("Name: " + name);
System.out.println("Marks: " + marks);
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student();
try {
student.setName("John");
student.setRollNumber(101);
student.setMarks(85);
student.display();
student.setMarks(150); // Invalid
} catch (InvalidMarksException e) {
System.out.println("Marks error: " + e.getMessage());
} catch (InvalidRollNumberException e) {
System.out.println("Roll number error: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Argument error: " + e.getMessage());
}
}
}
Exception Hierarchy
// Base custom exception
class ApplicationException extends Exception {
ApplicationException(String message) {
super(message);
}
}
// Specific exceptions
class DatabaseException extends ApplicationException {
DatabaseException(String message) {
super(message);
}
}
class ValidationException extends ApplicationException {
ValidationException(String message) {
super(message);
}
}
class NetworkException extends ApplicationException {
NetworkException(String message) {
super(message);
}
}
With Error Codes
class ErrorCodeException extends Exception {
private int errorCode;
ErrorCodeException(String message, int errorCode) {
super(message);
this.errorCode = errorCode;
}
int getErrorCode() {
return errorCode;
}
}
public class Main {
static void process(int code) throws ErrorCodeException {
if (code == 404) {
throw new ErrorCodeException("Not found", 404);
}
if (code == 500) {
throw new ErrorCodeException("Server error", 500);
}
}
public static void main(String[] args) {
try {
process(404);
} catch (ErrorCodeException e) {
System.out.println("Error " + e.getErrorCode() + ": " + e.getMessage());
}
}
}
Best Practices
- Name convention: XxxException
- Extend appropriate parent (Exception or RuntimeException)
- Provide constructors for different use cases
- Add useful information via fields/methods
- Document when exception is thrown
- Don’t create too many custom exceptions
When to Use
Create custom exception when:
- Domain-specific errors
- Need additional information
- Standard exceptions not sufficient
- Better error categorization
Use standard exceptions when:
- Common errors (null, illegal argument)
- Standard behavior needed
- No extra information required
Quick Reference
// Checked custom exception
class MyCheckedException extends Exception {
MyCheckedException(String message) {
super(message);
}
}
// Unchecked custom exception
class MyUncheckedException extends RuntimeException {
MyUncheckedException(String message) {
super(message);
}
}
// With additional data
class DetailedException extends Exception {
private int code;
DetailedException(String message, int code) {
super(message);
this.code = code;
}
int getCode() {
return code;
}
}
// Usage
void method() throws MyCheckedException {
throw new MyCheckedException("Error occurred");
}
Common Mistakes
// ✗ Wrong - extending Error
class MyException extends Error { // Don't extend Error
}
// ✗ Wrong - not calling super
class MyException extends Exception {
MyException(String message) {
// Missing super(message)
}
}
// ✓ Correct
class MyException extends Exception {
MyException(String message) {
super(message);
}
}
// ✗ Wrong - too generic name
class Exception1 extends Exception { }
// ✓ Correct - descriptive name
class InvalidPasswordException extends Exception { }
Exam Tips
Remember:
- Extend Exception for checked
- Extend RuntimeException for unchecked
- Call super() in constructor
- Naming: XxxException
- Provide multiple constructors
- Add custom fields for extra info
- Document with JavaDoc
- Use when standard exceptions insufficient
- Checked must be handled/declared
- Keep hierarchy organized
Common Questions:
- How to create custom exception?
- Checked vs unchecked custom?
- Parent class for custom exception?
- Multiple constructors?
- Additional information in exception?
- When to create custom exception?
- Naming convention?
- Exception hierarchy?
- Best practices?
- Common mistakes?