ZeroToSky
Java Tutorial 70 of 143
Basics
  • Introduction
  • Download and Install Java
  • Download and Install IntelliJ IDEA, Eclipse
  • Creating First Java Application in IntelliJ IDEA
  • JDK vs JRE vs JVM
  • Print output
  • Taking Input
  • Identifiers
  • Keywords
  • Variables
  • Data Types
  • Literals
  • Wrapper Classes
  • Operators
  • Decision Making
  • Loops and Jump Statements
  • Project: Number Guessing Game

All 17 lessons →

Methods
  • Introduction
  • Static Methods vs Instance Methods
  • Access Modifiers
  • Command Line Arguments
  • Variable Arguments (Varargs)

All 5 lessons →

Arrays
  • Introduction
  • One Dimensional Arrays
  • Multidimensional Arrays
  • Array Class
  • Arrays Class

All 5 lessons →

Strings
  • Introduction
  • String
  • StringBuffer
  • StringBuilder
  • String Class Methods
  • StringJoiner
  • StringTokenizer

All 7 lessons →

OOP & Interface

All 14 lessons →

Collections
  • Collection class
  • Collection Interface
  • List Interface
  • ArrayList
  • LinkedList
  • Set Interface
  • HashSet
  • LinkedHashSet
  • TreeSet
  • Queue Interface
  • PriorityQueue
  • Deque
  • ArrayDeque
  • Map Interface in Java
  • HashMap
  • LinkedHashMap
  • TreeMap
  • Hashtable
  • Iterator
  • Comparable and Comparator

All 20 lessons →

Exception Handling

All 7 lessons →

Multithreading & Concurrency
  • Introduction
  • Thread Class
  • Runnable Interface
  • Thread Life Cycle
  • Thread Priority
  • Synchronization
  • Inter-Thread Communication
  • Executor Framework
  • Callable and Future
  • Concurrent Collections

All 10 lessons →

Java 8 Features
  • Lambda Expressions
  • Functional Interfaces
  • Method References
  • Stream API
  • Optional Class
  • Default and Static Methods in Interface
  • Date and Time API

All 7 lessons →

Advanced Java Concepts
  • File Handling
  • Serialization and Deserialization
  • Reflection API
  • Annotations
  • Garbage Collection
  • Memory Management
  • JVM Architecture
  • Class Loading
  • Networking
  • Regular Expressions

All 10 lessons →

JDBC
  • Introduction
  • JDBC Driver
  • JDBC Connection
  • Types of Statements in JDBC
  • Transactions
  • ResultSet
  • Metadata
  • Connection Pooling

All 8 lessons →

GUI & Desktop
  • AWT
  • Swing
  • JavaFX

All 3 lessons →

Web & Enterprise
  • Servlets
  • JSP
  • Spring Framework
  • Spring Boot
  • Hibernate
  • REST API Development

All 6 lessons →

Practice Projects
  • Project: Number Guessing Game
  • Project: Banking System
  • Project: Student Management System
  • Project: Library Management System
  • Project: Quiz Application

All 5 lessons →

Quiz and Coding Practice
  • Java Topic-wise Quiz
  • Coding Practice Problems
  • Java Basics Coding Practice Problems
  • Java Exercises

All 4 lessons →

Interview Questions
  • Core Java Interview Questions and Answers
  • Advanced Java Interview Questions and Answers
  • Java Interview Questions

All 3 lessons →

DSA & Competitive Programming
  • Arrays and Strings
  • Linked List
  • Stack
  • Queue
  • Tree
  • Graph
  • Heap
  • Hashing
  • Sorting
  • Searching
  • Dynamic Programming
  • Roadmap for DSA in Java

All 12 lessons →

Java Exception Handling

Learn Java exception handling with try-catch, finally, throw, throws, custom exceptions, checked and unchecked exceptions, and practical examples.

Gaurav 7 min read Sign in to save
On this page

Java Exception Handling

Exception handling in Java is a mechanism for detecting and handling runtime problems so that a program can respond to unexpected situations without terminating abruptly.

For example, attempting to divide a number by zero causes an ArithmeticException:

int result = 10 / 0;

Instead of allowing the program to terminate because of the exception, Java provides constructs such as try, catch, finally, throw, and throws to handle exceptional situations.

What Is an Exception?

An exception is an event that occurs during program execution and disrupts the normal flow of the program.

For example:

int number = 10;
int result = number / 0;

This causes:

java.lang.ArithmeticException: / by zero

Without exception handling, the program may terminate when the exception is not handled.

Why Is Exception Handling Needed?

Exception handling helps applications:

  • Prevent unexpected termination
  • Handle runtime errors gracefully
  • Separate error-handling code from normal program logic
  • Provide meaningful error messages
  • Perform cleanup operations
  • Propagate errors to appropriate layers

For example, instead of:

int result = 10 / 0;

you can handle the condition:

try {
    int result = 10 / 0;
}
catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero");
}

Output:

Cannot divide by zero

Exception Hierarchy in Java

Java exceptions are part of a class hierarchy.

The basic structure is:

Object
   |
Throwable
   |
   +------------------+
   |                  |
 Error            Exception
                      |
              RuntimeException

Throwable is the root class for objects that can be thrown and caught.

It has two major branches:

  • Error
  • Exception

Error

Error represents serious problems that applications generally should not try to recover from.

Examples include:

  • OutOfMemoryError
  • StackOverflowError
  • NoClassDefFoundError

Application code normally should not use exception handling as a general solution for these conditions.

Exception

Exception represents conditions that applications may be able to handle.

Examples include:

  • IOException
  • SQLException
  • ClassNotFoundException
  • RuntimeException

Checked and Unchecked Exceptions

Java exceptions are commonly divided into:

  1. Checked exceptions
  2. Unchecked exceptions

Checked Exceptions

Checked exceptions are exceptions that the compiler requires you to handle or declare.

Examples include:

  • IOException
  • SQLException
  • FileNotFoundException
  • ClassNotFoundException

Example:

import java.io.FileReader;
import java.io.IOException;

class Main {

    public static void main(String[] args) {

        try {
            FileReader reader = new FileReader("data.txt");
        }
        catch (IOException e) {
            System.out.println("Unable to open file");
        }
    }
}

The compiler requires the checked exception to be handled or declared.

Unchecked Exceptions

Unchecked exceptions are subclasses of RuntimeException.

The compiler does not require them to be explicitly caught or declared.

Examples include:

  • ArithmeticException
  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException
  • NumberFormatException

Example:

int result = 10 / 0;

This causes an ArithmeticException.

Try-Catch

The try block contains code that may throw an exception.

The catch block handles the exception.

Syntax:

try {
    // Code that may throw an exception
}
catch (ExceptionType e) {
    // Handle exception
}

Example:

try {
    int result = 10 / 0;
    System.out.println(result);
}
catch (ArithmeticException e) {
    System.out.println("Division by zero is not allowed");
}

Output:

Division by zero is not allowed

When the exception occurs, the remaining statements in the try block are skipped and the matching catch block executes.

Multiple Catch Blocks

A try statement can have multiple catch blocks.

Example:

try {

    int[] numbers = {10, 20, 30};

    System.out.println(numbers[5]);
}
catch (ArithmeticException e) {
    System.out.println("Arithmetic error");
}
catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Array index is invalid");
}
catch (Exception e) {
    System.out.println("Some other exception occurred");
}

The most specific exception should be handled before a more general exception.

For example, this ordering is correct:

catch (ArrayIndexOutOfBoundsException e) {
}
catch (Exception e) {
}

But this is invalid:

catch (Exception e) {
}
catch (ArrayIndexOutOfBoundsException e) {
}

because Exception already catches the subclass.

Finally Block

The finally block contains code that is intended to execute after the try/catch processing.

It is commonly used for cleanup operations.

Example:

try {

    int result = 10 / 2;
    System.out.println(result);
}
catch (ArithmeticException e) {

    System.out.println("Arithmetic error");
}
finally {

    System.out.println("Execution completed");
}

Output:

5
Execution completed

The finally block generally executes whether or not an exception occurs.

Example Using Finally for Resource Cleanup

Consider a resource such as a file.

FileReader reader = null;

try {

    reader = new FileReader("data.txt");

}
catch (IOException e) {

    System.out.println("File error");

}
finally {

    if (reader != null) {
        try {
            reader.close();
        }
        catch (IOException e) {
            System.out.println("Unable to close file");
        }
    }
}

Modern Java commonly uses try-with-resources for resources that implement AutoCloseable, which is usually simpler and safer.

Try-With-Resources

Try-with-resources automatically closes resources after the try block finishes.

Example:

import java.io.FileReader;
import java.io.IOException;

class Main {

    public static void main(String[] args) {

        try (FileReader reader = new FileReader("data.txt")) {

            int data = reader.read();

            System.out.println(data);

        }
        catch (IOException e) {

            System.out.println("File error");
        }
    }
}

The FileReader is automatically closed when the try block completes.

This is preferred over manually closing many resources in a finally block.

The throw Keyword

The throw keyword is used to explicitly throw an exception.

Example:

public class Main {

    public static void main(String[] args) {

        int age = 15;

        if (age < 18) {
            throw new IllegalArgumentException("Age must be at least 18");
        }
    }
}

The program explicitly creates and throws an exception when the condition is invalid.

The throws Keyword

The throws keyword is used in a method declaration to indicate that the method may propagate specified exceptions to its caller.

Example:

import java.io.IOException;

class FileService {

    void readFile() throws IOException {
        // File operations
    }
}

The caller can then handle the exception:

try {
    FileService service = new FileService();
    service.readFile();
}
catch (IOException e) {
    System.out.println("File operation failed");
}

throw vs throws

throw throws
Used to explicitly throw an exception Used to declare exceptions a method may propagate
Used inside a method or block Used in a method declaration
Throws one exception object at a time Can declare multiple exception types
Example: throw new Exception() Example: void read() throws IOException

Example of throw:

throw new IllegalArgumentException("Invalid value");

Example of throws:

void read() throws IOException {
}

Creating Custom Exceptions

Java allows you to create your own exception classes.

A custom exception can extend Exception for a checked exception:

class InvalidAgeException extends Exception {

    public InvalidAgeException(String message) {
        super(message);
    }
}

Use it:

class Registration {

    static void register(int age) throws InvalidAgeException {

        if (age < 18) {
            throw new InvalidAgeException(
                "Age must be at least 18"
            );
        }

        System.out.println("Registration successful");
    }
}

Calling the method:

public class Main {

    public static void main(String[] args) {

        try {
            Registration.register(16);
        }
        catch (InvalidAgeException e) {
            System.out.println(e.getMessage());
        }
    }
}

Output:

Age must be at least 18

Custom Unchecked Exceptions

A custom unchecked exception can extend RuntimeException.

Example:

class InvalidAmountException extends RuntimeException {

    public InvalidAmountException(String message) {
        super(message);
    }
}

Use it:

class BankAccount {

    void withdraw(double amount) {

        if (amount <= 0) {
            throw new InvalidAmountException(
                "Amount must be greater than zero"
            );
        }
    }
}

Because the exception extends RuntimeException, callers are not required by the compiler to catch or declare it.

Common Java Exceptions

ArithmeticException

Occurs when an arithmetic operation is invalid.

int result = 10 / 0;

NullPointerException

Occurs when code attempts to use a null reference where an object is required.

String name = null;

System.out.println(name.length());

ArrayIndexOutOfBoundsException

Occurs when an invalid index is used with an array.

int[] numbers = {10, 20, 30};

System.out.println(numbers[5]);

NumberFormatException

Occurs when a string cannot be converted to the requested numeric type.

int number = Integer.parseInt("abc");

IllegalArgumentException

Indicates that a method has received an inappropriate argument.

throw new IllegalArgumentException("Invalid age");

Exception Propagation

If an exception is not handled in the current method, it can propagate up the call stack to the calling method.

Example:

class Main {

    static void method3() {
        int result = 10 / 0;
    }

    static void method2() {
        method3();
    }

    static void method1() {
        method2();
    }

    public static void main(String[] args) {

        try {
            method1();
        }
        catch (ArithmeticException e) {
            System.out.println("Exception handled in main");
        }
    }
}

The exception occurs in method3() but is eventually handled in main().

The propagation path is:

main()
   |
method1()
   |
method2()
   |
method3()
   |
ArithmeticException

Nested Try-Catch

A try block can be placed inside another try block.

Example:

try {

    try {

        int result = 10 / 0;
    }
    catch (ArithmeticException e) {

        System.out.println("Inner exception handled");
    }

}
catch (Exception e) {

    System.out.println("Outer exception handled");
}

Nested exception handling should be used only when it provides a clear separation of responsibilities.

Exception Methods

The Throwable class provides useful methods for understanding exceptions.

getMessage()

Returns the exception message.

catch (Exception e) {
    System.out.println(e.getMessage());
}

printStackTrace()

Prints the exception and its stack trace.

catch (Exception e) {
    e.printStackTrace();
}

toString()

Returns a string representation containing the exception type and message.

catch (Exception e) {
    System.out.println(e.toString());
}

Exception Chaining

Exception chaining allows one exception to preserve another exception as its cause.

Example:

try {

    // Some operation

}
catch (IOException e) {

    throw new RuntimeException(
        "Unable to process file",
        e
    );
}

The original IOException is preserved as the cause of the new exception.

This is useful when translating low-level exceptions into exceptions that make more sense at a higher application layer.

Multi-Catch

Java allows multiple exception types to be handled by a single catch block.

Example:

try {

    // Code that may throw different exceptions

}
catch (IOException | NumberFormatException e) {

    System.out.println("Operation failed");
}

This can reduce duplicate exception-handling code when the handling logic is the same.

Exception Handling Best Practices

Catch Specific Exceptions

Prefer:

catch (IOException e) {
}

over unnecessarily broad handling:

catch (Exception e) {
}

A specific exception communicates what the code expects and allows more appropriate handling.

Do Not Ignore Exceptions

Avoid empty catch blocks:

catch (Exception e) {
}

Ignoring exceptions can hide application problems.

At minimum, handle, log, or propagate the exception appropriately.

Do Not Use Exceptions for Normal Control Flow

Exceptions should generally represent exceptional situations rather than expected everyday branching.

Instead of relying on an exception to determine whether an operation normally succeeds, use appropriate validation or control flow.

Preserve the Original Cause

When wrapping an exception, preserve its cause:

throw new RuntimeException("Operation failed", e);

This makes debugging easier.

Use Try-With-Resources

For resources such as files, streams, and database-related objects that implement AutoCloseable, prefer try-with-resources:

try (FileReader reader = new FileReader("data.txt")) {
    // Use resource
}

This automatically manages resource cleanup.

Checked vs Unchecked Exceptions

Checked Exceptions Unchecked Exceptions
Usually subclasses of Exception excluding RuntimeException Subclasses of RuntimeException
Checked by the compiler Not required to be caught or declared
Must be caught or declared Can propagate without explicit declaration
Often represent conditions an application may reasonably handle Often indicate programming errors or invalid runtime conditions
Example: IOException Example: NullPointerException

The distinction is primarily about Java's compile-time checking rules.

finally vs Try-With-Resources

Both can be used for cleanup, but try-with-resources is generally preferred for objects that implement AutoCloseable.

Traditional approach:

FileReader reader = null;

try {
    reader = new FileReader("data.txt");
}
finally {
    if (reader != null) {
        reader.close();
    }
}

Try-with-resources:

try (FileReader reader = new FileReader("data.txt")) {
    // Use reader
}

The second approach is shorter and automatically handles resource closure.

Common Mistakes

Catching Exception Everywhere

Avoid using broad exception handling without a reason:

try {
    // code
}
catch (Exception e) {
    System.out.println("Error");
}

It can make it difficult to understand what actually went wrong.

Losing the Original Exception

Avoid replacing an exception without preserving its cause:

catch (IOException e) {
    throw new RuntimeException("Failed");
}

Prefer:

catch (IOException e) {
    throw new RuntimeException("Failed", e);
}

Using throws as a Replacement for Handling Everything

Declaring an exception with throws does not handle it. It passes responsibility to the caller.

void readFile() throws IOException {
}

The caller still needs to decide how the exception should be handled or propagated.

Catching the Wrong Exception Type

The exception type in a catch block must match the thrown exception or be one of its supertypes.

For example:

try {
    int result = 10 / 0;
}
catch (IOException e) {
    // Does not catch ArithmeticException
}

Key Points to Remember

  • An exception is an event that disrupts the normal execution of a program.
  • Java provides try, catch, finally, throw, and throws for exception handling.
  • Checked exceptions are subject to compile-time checking.
  • Unchecked exceptions are subclasses of RuntimeException.
  • throw explicitly throws an exception.
  • throws declares exceptions that a method may propagate.
  • Multiple catch blocks can handle different exception types.
  • finally is commonly used for cleanup.
  • Try-with-resources automatically closes AutoCloseable resources.
  • Custom exceptions can be created by extending Exception or RuntimeException.
  • Exceptions can propagate up the call stack.
  • Exception chaining preserves the original cause.
  • Specific exception handling is generally preferable to unnecessarily broad catches.

Conclusion

Exception handling in Java provides a structured way to deal with errors and unexpected runtime conditions. Using try, catch, finally, throw, and throws, developers can control how exceptional situations are detected, handled, and propagated.

For resource management, try-with-resources provides a convenient way to automatically close resources. For application-specific conditions, custom exceptions can make error handling clearer and more meaningful.

Effective exception handling should make failures easier to understand and recover from without hiding important problems in the application.

Related

Found a problem on this page, or something that could be clearer?

Goes straight to the author. Not published anywhere.