Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save carefree-ladka/129beb99c2e6ed0611e3c35a628b4560 to your computer and use it in GitHub Desktop.

Select an option

Save carefree-ladka/129beb99c2e6ed0611e3c35a628b4560 to your computer and use it in GitHub Desktop.
Java Exceptions: Checked, Unchecked, and User-Defined

Java Exceptions: Checked, Unchecked, and User-Defined

Table of Contents

  1. Introduction to Exceptions
  2. Exception Hierarchy
  3. Checked Exceptions
  4. Unchecked Exceptions
  5. User-Defined Exceptions
  6. Comparison Table
  7. Exception Handling Best Practices

Introduction to Exceptions

Exceptions in Java are events that disrupt the normal flow of program execution. They represent error conditions that occur during runtime. Java provides a robust exception handling mechanism to handle these exceptional situations gracefully.

Exception Hierarchy

All exception classes in Java are derived from the Throwable class. The hierarchy looks like this:

Throwable
├── Error (unchecked)
└── Exception
    ├── RuntimeException (unchecked)
    └── Other Exceptions (checked)

Checked Exceptions

What are Checked Exceptions?

Checked exceptions are exceptions that are checked at compile-time. The compiler forces you to either handle these exceptions using a try-catch block or declare them using the throws keyword. These exceptions represent conditions that a well-written application should anticipate and recover from.

Key Characteristics:

  • Must be handled or declared
  • Checked at compile-time
  • Extend Exception class (but not RuntimeException)
  • Represent recoverable conditions

Common Checked Exceptions

// IOException
import java.io.*;

public class FileExample {
    public void readFile(String filename) throws IOException {
        FileReader file = new FileReader(filename);
        BufferedReader reader = new BufferedReader(file);
        String line = reader.readLine();
        reader.close();
    }
}

// SQLException
import java.sql.*;

public class DatabaseExample {
    public void queryDatabase() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    }
}

// ClassNotFoundException
public class ClassExample {
    public void loadClass() throws ClassNotFoundException {
        Class.forName("com.example.MyClass");
    }
}

Handling Checked Exceptions

Option 1: Try-Catch Block

public class CheckedExceptionHandling {
    public void readFile(String filename) {
        try {
            FileReader file = new FileReader(filename);
            BufferedReader reader = new BufferedReader(file);
            String line = reader.readLine();
            System.out.println(line);
            reader.close();
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        }
    }
}

Option 2: Throws Declaration

public class CheckedExceptionThrows {
    public void readFile(String filename) throws IOException {
        FileReader file = new FileReader(filename);
        BufferedReader reader = new BufferedReader(file);
        String line = reader.readLine();
        reader.close();
    }
    
    public static void main(String[] args) {
        CheckedExceptionThrows example = new CheckedExceptionThrows();
        try {
            example.readFile("data.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Unchecked Exceptions

What are Unchecked Exceptions?

Unchecked exceptions are exceptions that are not checked at compile-time. These are also called runtime exceptions. They extend RuntimeException class and represent programming errors that could have been avoided.

Key Characteristics:

  • Not required to be handled or declared
  • Occur at runtime
  • Extend RuntimeException class
  • Represent programming bugs

Common Unchecked Exceptions

// NullPointerException
public class NullPointerExample {
    public void demonstrateNPE() {
        String str = null;
        System.out.println(str.length()); // Throws NullPointerException
    }
}

// ArrayIndexOutOfBoundsException
public class ArrayIndexExample {
    public void demonstrateArrayIndex() {
        int[] numbers = {1, 2, 3};
        System.out.println(numbers[5]); // Throws ArrayIndexOutOfBoundsException
    }
}

// ArithmeticException
public class ArithmeticExample {
    public void demonstrateArithmetic() {
        int result = 10 / 0; // Throws ArithmeticException
    }
}

// IllegalArgumentException
public class IllegalArgumentExample {
    public void setAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}

// NumberFormatException
public class NumberFormatExample {
    public void parseNumber() {
        int num = Integer.parseInt("abc"); // Throws NumberFormatException
    }
}

When to Use Unchecked Exceptions

Unchecked exceptions should be used for:

  • Programming errors that should be fixed in code
  • Validation failures
  • Invalid arguments
  • Precondition violations
public class UncheckedExceptionExample {
    public double calculateSquareRoot(double number) {
        if (number < 0) {
            throw new IllegalArgumentException("Cannot calculate square root of negative number");
        }
        return Math.sqrt(number);
    }
    
    public void processArray(int[] arr, int index) {
        if (arr == null) {
            throw new NullPointerException("Array cannot be null");
        }
        if (index < 0 || index >= arr.length) {
            throw new IndexOutOfBoundsException("Index out of valid range");
        }
        // Process array
    }
}

User-Defined Exceptions

Creating Custom Exceptions

User-defined exceptions allow you to create application-specific exception types that provide more meaningful error information.

Custom Checked Exception:

// Extends Exception (checked)
public class InsufficientFundsException extends Exception {
    private double amount;
    
    public InsufficientFundsException(double amount) {
        super("Insufficient funds. Required: $" + amount);
        this.amount = amount;
    }
    
    public InsufficientFundsException(String message, double amount) {
        super(message);
        this.amount = amount;
    }
    
    public double getAmount() {
        return amount;
    }
}

// Usage
public class BankAccount {
    private double balance;
    
    public BankAccount(double balance) {
        this.balance = balance;
    }
    
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            double shortfall = amount - balance;
            throw new InsufficientFundsException(shortfall);
        }
        balance -= amount;
        System.out.println("Withdrawal successful. Remaining balance: $" + balance);
    }
    
    public static void main(String[] args) {
        BankAccount account = new BankAccount(500.0);
        try {
            account.withdraw(600.0);
        } catch (InsufficientFundsException e) {
            System.err.println("Transaction failed: " + e.getMessage());
            System.err.println("Shortfall: $" + e.getAmount());
        }
    }
}

Custom Unchecked Exception:

// Extends RuntimeException (unchecked)
public class InvalidAgeException extends RuntimeException {
    private int age;
    
    public InvalidAgeException(int age) {
        super("Invalid age: " + age);
        this.age = age;
    }
    
    public InvalidAgeException(String message, int age) {
        super(message);
        this.age = age;
    }
    
    public int getAge() {
        return age;
    }
}

// Usage
public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        setAge(age);
    }
    
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new InvalidAgeException("Age must be between 0 and 150", age);
        }
        this.age = age;
    }
    
    public static void main(String[] args) {
        try {
            Person person = new Person("John", -5);
        } catch (InvalidAgeException e) {
            System.err.println("Error: " + e.getMessage());
            System.err.println("Invalid age provided: " + e.getAge());
        }
    }
}

Best Practices

When creating user-defined exceptions:

  1. Choose the right parent class: Extend Exception for checked exceptions, RuntimeException for unchecked
  2. Provide constructors: Include default constructor and one with a message parameter
  3. Add relevant fields: Store additional context information
  4. Use meaningful names: Exception names should end with "Exception"
  5. Include detailed messages: Help developers understand what went wrong
public class DataValidationException extends Exception {
    private String fieldName;
    private Object invalidValue;
    
    // Default constructor
    public DataValidationException() {
        super();
    }
    
    // Constructor with message
    public DataValidationException(String message) {
        super(message);
    }
    
    // Constructor with message and cause
    public DataValidationException(String message, Throwable cause) {
        super(message, cause);
    }
    
    // Constructor with all details
    public DataValidationException(String message, String fieldName, Object invalidValue) {
        super(message);
        this.fieldName = fieldName;
        this.invalidValue = invalidValue;
    }
    
    public String getFieldName() {
        return fieldName;
    }
    
    public Object getInvalidValue() {
        return invalidValue;
    }
}

Examples

E-commerce Order Processing:

public class OrderProcessingException extends Exception {
    private String orderId;
    private String errorCode;
    
    public OrderProcessingException(String message, String orderId, String errorCode) {
        super(message);
        this.orderId = orderId;
        this.errorCode = errorCode;
    }
    
    public String getOrderId() {
        return orderId;
    }
    
    public String getErrorCode() {
        return errorCode;
    }
}

public class OrderService {
    public void processOrder(String orderId) throws OrderProcessingException {
        // Validation logic
        if (orderId == null || orderId.isEmpty()) {
            throw new OrderProcessingException(
                "Order ID cannot be empty",
                orderId,
                "ORD_001"
            );
        }
        
        // Payment processing
        if (!processPayment(orderId)) {
            throw new OrderProcessingException(
                "Payment processing failed",
                orderId,
                "PAY_001"
            );
        }
    }
    
    private boolean processPayment(String orderId) {
        // Payment logic
        return false;
    }
}

Configuration Exception:

public class ConfigurationException extends RuntimeException {
    private String configKey;
    
    public ConfigurationException(String message, String configKey) {
        super(message);
        this.configKey = configKey;
    }
    
    public ConfigurationException(String message, String configKey, Throwable cause) {
        super(message, cause);
        this.configKey = configKey;
    }
    
    public String getConfigKey() {
        return configKey;
    }
}

public class ApplicationConfig {
    public String getRequiredProperty(String key) {
        String value = System.getProperty(key);
        if (value == null) {
            throw new ConfigurationException(
                "Required configuration property not found: " + key,
                key
            );
        }
        return value;
    }
}

Comparison Table

Feature Checked Exception Unchecked Exception User-Defined Exception
Parent Class Exception RuntimeException Either Exception or RuntimeException
Compile-time Check Yes No Depends on parent class
Must Handle/Declare Yes No Depends on parent class
Use Case Recoverable conditions Programming errors Application-specific errors
Examples IOException, SQLException NullPointerException, ArithmeticException InsufficientFundsException, InvalidAgeException
When to Use External failures you can recover from Logic errors that shouldn't happen Domain-specific error conditions

Exception Handling Best Practices

  1. Be specific with catch blocks: Catch specific exceptions rather than generic Exception
try {
    // code
} catch (FileNotFoundException e) {
    // Handle file not found
} catch (IOException e) {
    // Handle other IO errors
}
  1. Don't swallow exceptions: Always log or handle exceptions appropriately
// Bad
try {
    riskyOperation();
} catch (Exception e) {
    // Silent failure
}

// Good
try {
    riskyOperation();
} catch (Exception e) {
    logger.error("Operation failed", e);
    // Handle appropriately
}
  1. Use finally for cleanup: Always close resources in finally block or use try-with-resources
// Try-with-resources (Java 7+)
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
} catch (IOException e) {
    e.printStackTrace();
}
  1. Don't use exceptions for flow control: Exceptions should be exceptional

  2. Provide context in exception messages: Include relevant information to help debugging

throw new IllegalArgumentException("Invalid age: " + age + ". Must be between 0 and 150");
  1. Document exceptions: Use @throws in Javadoc
/**
 * Withdraws money from the account
 * @param amount the amount to withdraw
 * @throws InsufficientFundsException if balance is insufficient
 */
public void withdraw(double amount) throws InsufficientFundsException {
    // implementation
}

This guide covers the fundamental concepts of Java exceptions. Understanding when to use checked vs unchecked exceptions and how to create meaningful custom exceptions is crucial for writing robust Java applications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment