Checked vs Unchecked Exceptions in Java – Complete Information

Overview

In Java, exceptions are divided into two main categories: Checked Exceptions and Unchecked Exceptions. This distinction is fundamental to Java's exception handling mechanism.

Exception Hierarchy

Throwable
├── Error (Unchecked)
│   ├── VirtualMachineError
│   │   ├── OutOfMemoryError
│   │   └── StackOverflowError
│   └── ...
├── Exception
│   ├── RuntimeException (Unchecked)
│   │   ├── NullPointerException
│   │   ├── IllegalArgumentException
│   │   ├── ArrayIndexOutOfBoundsException
│   │   └── ...
│   └── Checked Exceptions
│       ├── IOException
│       ├── SQLException
│       ├── ClassNotFoundException
│       └── ...

Checked Exceptions

Definition

Checked exceptions are exceptions that are checked at compile-time. The compiler ensures that these exceptions are either caught or declared in the method signature.

Characteristics

  • Must be handled explicitly using try-catch or declared with throws
  • Represent conditions that a reasonable application might want to catch
  • Extend Exception but not RuntimeException

Common Checked Exceptions

import java.io.*;
import java.sql.*;
public class CheckedExceptionsExamples {
// IOException - Must be handled or declared
public void readFile() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
String line = reader.readLine();
reader.close();
}
// SQLException - Must be handled or declared
public void databaseOperation() throws SQLException {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db");
Statement stmt = conn.createStatement();
stmt.executeQuery("SELECT * FROM users");
}
// ClassNotFoundException - Must be handled or declared
public void loadClass() throws ClassNotFoundException {
Class.forName("com.example.MyClass");
}
// FileNotFoundException - Must be handled or declared
public void openFile() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("nonexistent.txt");
}
}

Handling Checked Exceptions

public class CheckedExceptionHandling {
// Method 1: Declare in throws clause
public void methodWithThrows() throws IOException {
FileReader file = new FileReader("test.txt");
}
// Method 2: Handle with try-catch
public void methodWithTryCatch() {
try {
FileReader file = new FileReader("test.txt");
System.out.println("File opened successfully");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
}
}
// Method 3: Combination
public void processFile() throws IOException {
try {
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
// Process line
} catch (FileNotFoundException e) {
System.err.println("Logging error: " + e.getMessage());
throw e; // Re-throw
}
}
}

Unchecked Exceptions

Definition

Unchecked exceptions are exceptions that are not checked at compile-time. They extend RuntimeException or Error.

Characteristics

  • Not required to be caught or declared
  • Represent programming errors or system problems
  • Extend RuntimeException or Error

Common Unchecked Exceptions

public class UncheckedExceptionsExamples {
public void demonstrateUncheckedExceptions() {
// NullPointerException
String str = null;
// str.length(); // This would throw NullPointerException
// ArrayIndexOutOfBoundsException
int[] numbers = {1, 2, 3};
// int value = numbers[5]; // This would throw ArrayIndexOutOfBoundsException
// IllegalArgumentException
// setAge(-5); // This would throw IllegalArgumentException
// NumberFormatException
// int num = Integer.parseInt("abc"); // This would throw NumberFormatException
// ClassCastException
Object obj = "Hello";
// Integer num = (Integer) obj; // This would throw ClassCastException
}
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative: " + age);
}
// Set age
}
}

Error Examples

public class ErrorExamples {
public void causeStackOverflow() {
causeStackOverflow(); // Recursive call without base case
}
public void causeOutOfMemory() {
List<byte[]> memoryHog = new ArrayList<>();
while (true) {
memoryHog.add(new byte[1024 * 1024]); // 1MB chunks
}
}
}

Key Differences

AspectChecked ExceptionsUnchecked Exceptions
Compile-time CheckingYesNo
Handling RequirementMust be handled or declaredOptional
InheritanceExtend ExceptionExtend RuntimeException or Error
RecoveryUsually recoverableUsually programming errors
ExamplesIOException, SQLExceptionNullPointerException, IllegalArgumentException

Best Practices

When to Use Checked Exceptions

public class CheckedExceptionBestPractices {
// Use checked exceptions for recoverable conditions
public void loadConfiguration(String filename) throws ConfigurationException {
try {
Properties props = new Properties();
props.load(new FileInputStream(filename));
} catch (IOException e) {
throw new ConfigurationException("Failed to load configuration: " + filename, e);
}
}
// Provide meaningful exception messages
public void validateUserInput(String input) throws ValidationException {
if (input == null || input.trim().isEmpty()) {
throw new ValidationException("Input cannot be null or empty");
}
if (input.length() < 5) {
throw new ValidationException("Input must be at least 5 characters long");
}
}
}
// Custom checked exception
class ConfigurationException extends Exception {
public ConfigurationException(String message) {
super(message);
}
public ConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
class ValidationException extends Exception {
public ValidationException(String message) {
super(message);
}
}

When to Use Unchecked Exceptions

public class UncheckedExceptionBestPractices {
// Use unchecked exceptions for programming errors
public void processOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getItems() == null || order.getItems().isEmpty()) {
throw new IllegalStateException("Order must have at least one item");
}
// Process order
}
// Precondition validation
public void withdrawMoney(BankAccount account, double amount) {
if (account == null) {
throw new NullPointerException("Account cannot be null");
}
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive: " + amount);
}
if (amount > account.getBalance()) {
throw new InsufficientFundsException("Insufficient funds");
}
account.withdraw(amount);
}
}
// Custom unchecked exception
class InsufficientFundsException extends RuntimeException {
public InsufficientFundsException(String message) {
super(message);
}
}

Exception Handling Patterns

1. Try-with-resources (Java 7+)

public class TryWithResourcesExample {
// Automatic resource management for checked exceptions
public void readFile(String filename) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// Handle IOException
System.err.println("Error reading file: " + e.getMessage());
}
}
}

2. Exception Translation

public class ExceptionTranslation {
// Translate lower-level exceptions to higher-level abstractions
public User loadUser(String userId) throws UserServiceException {
try {
return userRepository.findById(userId);
} catch (SQLException e) {
throw new UserServiceException("Failed to load user: " + userId, e);
} catch (IOException e) {
throw new UserServiceException("Configuration error while loading user", e);
}
}
}
class UserServiceException extends Exception {
public UserServiceException(String message) {
super(message);
}
public UserServiceException(String message, Throwable cause) {
super(message, cause);
}
}

3. Exception Chaining

public class ExceptionChainingExample {
public void processData(String data) throws ProcessingException {
try {
// Some processing that might throw IOException
parseData(data);
} catch (IOException e) {
// Chain the exception
throw new ProcessingException("Failed to process data: " + data, e);
}
}
private void parseData(String data) throws IOException {
// Parsing logic
}
}
class ProcessingException extends Exception {
public ProcessingException(String message) {
super(message);
}
public ProcessingException(String message, Throwable cause) {
super(message, cause);
}
}

Common Mistakes to Avoid

public class ExceptionMistakes {
// ❌ DON'T: Empty catch block (swallowing exceptions)
public void badPractice1() {
try {
// Some code that might throw exception
} catch (Exception e) {
// Empty - exception completely ignored!
}
}
// ❌ DON'T: Catch generic Exception
public void badPractice2() {
try {
// Some code
} catch (Exception e) {
// Too broad - might catch unexpected exceptions
}
}
// ❌ DON'T: Throw Exception in method signature
public void badPractice3() throws Exception {
// Too vague - callers don't know what to expect
}
// ✅ DO: Be specific about exceptions
public void goodPractice1() throws IOException, SQLException {
// Caller knows exactly what to handle
}
// ✅ DO: Log exceptions properly
public void goodPractice2() {
try {
// Some code
} catch (SpecificException e) {
logger.error("Specific error occurred", e);
// Handle or re-throw appropriately
}
}
// ✅ DO: Use finally for cleanup
public void goodPractice3() {
Connection conn = null;
try {
conn = getConnection();
// Use connection
} catch (SQLException e) {
logger.error("Database error", e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
logger.error("Error closing connection", e);
}
}
}
}
}

Performance Considerations

  • Checked exceptions have minimal performance overhead
  • Unchecked exceptions are relatively fast when not thrown
  • Stack trace generation is expensive - avoid exceptions for normal control flow
  • Use exceptions for exceptional circumstances only

Summary

  • Use Checked Exceptions for recoverable conditions that callers should handle
  • Use Unchecked Exceptions for programming errors and precondition violations
  • Always document exceptions that your methods can throw
  • Provide meaningful error messages and context
  • Don't use exceptions for normal control flow
  • Consider the caller's perspective when choosing exception types

Understanding the distinction between checked and unchecked exceptions is crucial for writing robust, maintainable Java applications that handle errors appropriately.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper