DEV Community

Cover image for Thinking about Exceptions
Alexandre Pontes
Alexandre Pontes

Posted on

Thinking about Exceptions

Let's Think a Little Bit About Exceptions

What Is an Exception?

An exception is an event that occurs unexpectedly and disrupts the normal flow of a program. We never write code thinking, "This should be a good exception!" On the contrary, exceptions happen when something goes wrong beyond our control or planning.

Therefore, handling exceptions properly is crucial to ensuring that our system remains robust and reliable.

Checked vs. Unchecked Exceptions

Whether you're using Java or Kotlin, exceptions are an essential part of the language. Let's explore the hierarchy behind exception handling.

Checked Exceptions

Checked exceptions are exceptions that are verified at compile time. This means that the compiler forces you to handle them either by using a try-catch block or by propagating them with the throws keyword. The base class for checked exceptions is Exception (excluding RuntimeException and its subclasses).

Examples: IOException, SQLException, FileNotFoundException.

Unchecked Exceptions

Unchecked exceptions occur during runtime and are not checked at compile time. They usually arise due to logical errors in the program, such as accessing a null reference or an invalid index. The base class for unchecked exceptions is RuntimeException.

Examples: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException.

Handling and Throwing Exceptions

In Java, you can either handle exceptions directly or propagate them to the caller. Let’s look at both approaches.

Handling Exceptions

The most common way to handle exceptions is by using a try-catch-finally block:

try {
    yourMethod(); 
    // Additional logic
} catch (IOException e) {
    // Log the stack trace or throw a custom exception
    throw new CustomException("Error executing the method", e);
} finally {
    // Cleanup resources, such as closing files or database connections
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Best Practice: Avoid catching general exceptions like Exception. Instead, catch specific exceptions to make your error handling more precise.

Throwing Exceptions

Instead of handling an exception within the method, you can propagate it to the caller using the throws keyword:

public void yourMethod() throws IOException {
    Files.readAllLines(Paths.get("file.txt"));
}
Enter fullscreen mode Exit fullscreen mode

This approach allows the calling method to decide how to handle the exception.

Unit Test Example

A unit test can validate that a method correctly throws an exception using JUnit:

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

class ExceptionTest {
    @Test
    void testExceptionIsThrown() {
        ExceptionThrowingClass obj = new ExceptionThrowingClass();

        assertThrows(IllegalArgumentException.class, () -> {
            obj.methodThatThrowsException();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

This test ensures that methodThatThrowsException correctly throws an IllegalArgumentException when executed.

Final Thoughts

Handling exceptions properly makes your code more reliable and maintainable. When choosing between checked and unchecked exceptions, think about the best way to communicate failures to users and developers. Also, whenever possible, prefer throwing custom exceptions to make error handling clearer and more meaningful.

Happy coding! πŸš€

Top comments (0)