;
Java Intermediate July 25 ,2025

 Exception Handling

1. Introduction 

Exception handling in Java is a powerful mechanism that handles runtime errors, maintains the normal flow of the application, and prevents the program from crashing unexpectedly.

2. What is an Exception?

An exception is an unwanted or unexpected event that disrupts the normal flow of a program. It is an object which is thrown at runtime.

Example: Dividing a number by zero results in an ArithmeticException.

int result = 10 / 0; // Throws ArithmeticException

3. Why Exception Handling is Needed?

  • To prevent abnormal termination.
  • To separate error-handling code from regular code.
  • To gracefully handle errors.
  • To maintain the application flow.

4. Types of Exceptions in Java

In Java, exceptions are events that disrupt the normal flow of a program’s execution. They are objects that represent an error or an unexpected behavior during program execution.

Java categorizes exceptions into three main types:

a. Checked Exceptions

Definition:

Checked exceptions are those that are checked at compile-time.
The compiler ensures that the programmer handles these exceptions either using a try-catch block or by declaring them using the throws keyword.

If not handled properly, the program will not compile.

When to Use:

  • When the program needs to recover from the exception or inform the user gracefully.
  • Typically used for external events or resources, like file handling, network access, or database connections.

Common Examples:

  • IOException
  • SQLException
  • FileNotFoundException
  • ClassNotFoundException

Example:

import java.io.*;

public class CheckedExample {
    public static void main(String[] args) {
        try {
            FileReader fr = new FileReader("file.txt");  // may throw FileNotFoundException
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + e.getMessage());
        }
    }
}

Explanation:

  • The compiler forces you to handle FileNotFoundException because it’s a checked exception.
  • This ensures safer code that anticipates potential problems.

b. Unchecked Exceptions

Definition:

Unchecked exceptions are not checked at compile-time.
These exceptions occur due to programming errors such as logic mistakes or incorrect API usage.

The compiler does not force you to handle them, but it's good practice to anticipate and handle them where appropriate.

When to Use:

  • Typically represent bugs in the code.
  • Can be avoided through better coding practices and input validation.

Common Examples:

  • ArithmeticException
  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException

Example:

public class UncheckedExample {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int result = a / b;  // throws ArithmeticException
        System.out.println("Result: " + result);
    }
}

Explanation:

  • Dividing by zero throws an ArithmeticException, which is unchecked.
  • Program will compile, but fail at runtime unless handled properly.

c. Errors

Definition:

Errors are serious issues that occur in the JVM (Java Virtual Machine) and are not meant to be handled by the application code.

They typically indicate system-level problems that are beyond the control of the application, such as memory overflows or JVM crashes.

When to Use:

  • You should not attempt to catch or handle Errors in most cases.
  • Let the JVM handle them.

Common Examples:

  • OutOfMemoryError
  • StackOverflowError
  • VirtualMachineError

Example:

public class ErrorExample {
    public static void recursive() {
        recursive();  // causes StackOverflowError
    }

    public static void main(String[] args) {
        recursive();
    }
}

Explanation:

  • The recursive call goes on infinitely, eventually exhausting the call stack and causing a StackOverflowError.
  • This is not something you typically catch with a try-catch block.

Summary Table

TypeChecked atRecoverable?Common Examples
Checked ExceptionCompile-TimeYesIOException, SQLException
Unchecked ExceptionRuntimeOften YesNullPointerException, ArithmeticException
ErrorRuntimeNoOutOfMemoryError, StackOverflowError

 

5. Java Exception Hierarchy

In Java, all exceptions and errors are part of a class hierarchy rooted in the class java.lang.Object.
The topmost class in this hierarchy for handling errors and exceptions is Throwable, which has two direct subclasses:

Exception Hierarchy in Java - Naukri Code 360
  1. Exception
  2. Error

Hierarchy Structure:

java.lang.Object  
   └── java.lang.Throwable  
         ├── java.lang.Error (unchecked)  
         └── java.lang.Exception  
               ├── Checked Exceptions  
               └── java.lang.RuntimeException (unchecked)

 1. Throwable (Superclass for All Errors and Exceptions)

  • Throwable is the superclass for all errors and exceptions in Java.
  • It provides common methods like getMessage(), printStackTrace(), and toString().
  • Only objects that are instances of Throwable or its subclasses can be thrown using the throw statement or caught using try-catch.

 2. Error (Unchecked)

  • Subclass of Throwable.
  • Represents serious issues that are not intended to be caught by applications.
  • Mostly caused by the JVM or system-level problems.
  • Not recoverable through program logic.

Common Subclasses of Error:

  • OutOfMemoryError
  • StackOverflowError
  • VirtualMachineError
  • AssertionError

Example:

public class ErrorExample {
    public static void main(String[] args) {
        recursive();  // StackOverflowError
    }

    public static void recursive() {
        recursive();
    }
}

 3. Exception (Can Be Checked or Unchecked)

  • Also a subclass of Throwable.
  • Represents conditions that a program might want to catch and handle.
  • Divided into:
    • Checked Exceptions
    • Unchecked Exceptions (RuntimeException and its subclasses)

 3.1 Checked Exceptions

  • Checked at compile-time.
  • Must be handled using try-catch or declared using throws.
Common Checked Exceptions:
  • IOException
  • SQLException
  • FileNotFoundException
  • ClassNotFoundException
Example:
import java.io.*;

public class CheckedExample {
    public static void main(String[] args) {
        try {
            FileReader fr = new FileReader("file.txt");
        } catch (FileNotFoundException e) {
            System.out.println("File not found!");
        }
    }
}

 3.2 Unchecked Exceptions (RuntimeException and its subclasses)

  • Not checked at compile-time.
  • Caused by programming logic errors and can occur during program execution.
  • Program compiles fine but may crash at runtime if not handled.
Common Unchecked Exceptions:
  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException
Example:
public class UncheckedExample {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length());  // NullPointerException
    }
}

 Summary Table:

ClassTypeChecked atRecoverable?Examples
ThrowableBase class
ErrorUncheckedRuntimeNoOutOfMemoryError, StackOverflowError
ExceptionGeneral exceptionCompile/RunYes
→ Checked ExceptionChecked ExceptionCompile-TimeYesIOException, SQLException
→ RuntimeExceptionUncheckedRuntimeOften YesNullPointerException, ArithmeticException

 Visual Representation:

Object
 └── Throwable
      ├── Error (not meant to be caught)
      │    └── OutOfMemoryError, StackOverflowError
      └── Exception
           ├── Checked Exceptions (must be handled)
           │    └── IOException, SQLException
           └── RuntimeException (unchecked)
                └── NullPointerException, ArithmeticException

6. Difference Between Errors and Exceptions

FeatureExceptionError
RecoverableYesNo
Handled by programYesNo
ExampleFileNotFoundExceptionOutOfMemoryError

7. Basic Syntax: try, catch, finally

Exception handling in Java is done using the following keywords:

  • try
  • catch
  • finally
  • throw
  • throws

In this section, we’ll focus on the basic structure of try-catch-finally blocks.

Syntax:

try {
    // code that may throw an exception
} catch (ExceptionType name) {
    // code to handle the exception
} finally {
    // code that will always execute
}

Explanation of Each Block:

 try block

  • The try block contains code that might throw an exception.
  • If an exception occurs, control is transferred to the corresponding catch block.
  • If no exception occurs, the catch block is skipped.
try {
    int result = 10 / 0; // risky code
}

 catch block

  • The catch block handles the exception.
  • You can have multiple catch blocks to handle different types of exceptions separately.
  • If the exception matches the type declared in the catch block, that block will execute.
catch (ArithmeticException e) {
    System.out.println("Cannot divide by zero.");
}

 finally block

  • The finally block contains code that always executes, whether an exception is thrown or not.
  • Typically used for resource cleanup, like closing files, database connections, or network sockets.
finally {
    System.out.println("Finally block is always executed.");
}

Complete Example:

public class TryCatchFinallyExample {
    public static void main(String[] args) {
        try {
            int num = 10;
            int result = num / 0;  // This will throw ArithmeticException
            System.out.println("Result: " + result);
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e.getMessage());
        } finally {
            System.out.println("This block always executes.");
        }
    }
}

Output:

Exception caught: / by zero
This block always executes.

Key Points:

  • The try block must be followed by either a catch or a finally block (or both).
  • If no exception occurs:
    • catch is skipped.
    • finally is still executed.
  • If an exception occurs:
    • The matching catch block handles it.
    • Then finally executes.
  • If an exception occurs and is not caught, the program still runs the finally block before termination.

8. Working of try-catch Block

public class Example {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            System.out.println("Cannot divide by zero");
        }
    }
}

Output:

Cannot divide by zero

9. The finally Block

  • Used to execute important code such as closing resources.
  • Executes regardless of whether an exception is caught or not.
try {
    int data = 10 / 2;
} catch (Exception e) {
    System.out.println(e);
} finally {
    System.out.println("finally block executed");
}

Output:

finally block executed

10. Multiple catch Blocks

Java allows multiple catch blocks to handle different types of exceptions that may occur in a try block. When an exception is thrown, only the first matching catch block is executed. In the example, an ArithmeticException occurs first, so that block is executed before checking others.

try {
    int arr[] = new int[5];
    arr[5] = 30 / 0;
} catch (ArithmeticException e) {
    System.out.println("Arithmetic Exception");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Array Index Out Of Bounds Exception");
}

Output:

Arithmetic Exception

11. Nested try-catch Blocks

Java supports nesting of try-catch blocks. This is useful when a specific piece of code needs its own error handling inside a larger block. The inner try handles its exception independently, and the outer catch is triggered only if an exception is not caught by the inner block.

try {
    try {
        int result = 50 / 0;
    } catch (ArithmeticException e) {
        System.out.println("Inner catch: " + e);
    }
} catch (Exception e) {
    System.out.println("Outer catch: " + e);
}

Output:

Inner catch: java.lang.ArithmeticException: / by zero

12. throw Keyword

The throw keyword is used to explicitly throw an exception from a method or block of code. It is typically used for custom logic or error signaling. In the example, an ArithmeticException is manually thrown with a custom message.

public class Test {
    public static void main(String[] args) {
        throw new ArithmeticException("Demo Exception");
    }
}

Output:

Exception in thread "main" java.lang.ArithmeticException: Demo Exception

13. throws Keyword

The throws keyword is used in method declarations to indicate that the method might throw one or more exceptions. It informs the calling code to handle or propagate those exceptions. This is mostly used for checked exceptions like IOException.

void readFile() throws IOException {
    FileReader fr = new FileReader("file.txt");
}

14. Custom Exceptions (User-defined)

Java allows users to define their own exception classes by extending the Exception class. This is useful for application-specific error handling. The custom exception can carry a meaningful message to make debugging easier, as shown in the example.

class MyException extends Exception {
    MyException(String message) {
        super(message);
    }
}

public class Demo {
    public static void main(String[] args) throws MyException {
        throw new MyException("Custom Exception occurred");
    }
}

Output:

Exception in thread "main" MyException: Custom Exception occurred

How to Create User-Defined (Custom) Exceptions in Java

Java allows you to define your own exceptions when the standard ones do not suit your application's error handling needs. This helps in making your code more readable and specific to the business logic.

Steps to Create a User-Defined Exception

Step 1: Create a class that extends Exception or RuntimeException

class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
}
  • Extend Exception for a checked exception (must be handled with try-catch or throws).
  • Extend RuntimeException for an unchecked exception (not mandatory to handle).

Step 2: Use the throw keyword to throw the exception

throw new MyException("This is a custom exception");

Step 3: If it's a checked exception, declare it using throws in the method signature

public void process() throws MyException {
    // some code
}

Step 4: Handle it using try-catch

try {
    process();
} catch (MyException e) {
    System.out.println("Caught: " + e.getMessage());
}

When to Create Custom Exceptions

  • When existing Java exceptions do not clearly describe the issue.
  • When you need application-specific error messages and types.
  • When you want better organization of exception handling in a large application.

Example Use Case: Age Verification for Voting

// Step 1: Define the custom exception
class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

// Step 2: Use the custom exception in logic
public class VoteEligibility {
    static void checkAge(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("You must be at least 18 years old to vote.");
        } else {
            System.out.println("You are eligible to vote.");
        }
    }

    // Step 3: Handle the exception
    public static void main(String[] args) {
        try {
            checkAge(15);
        } catch (InvalidAgeException e) {
            System.out.println("Exception caught: " + e.getMessage());
        }
    }
}

Output:

Exception caught: You must be at least 18 years old to vote.

If the age is changed to 20, the output would be:

You are eligible to vote.

Exception Handling Enhancements in Java 8

Although Java 8 did not introduce new exception types, it brought functional programming features that improved how developers write cleaner and more concise error-handling code. These include:

1. Using Lambda Expressions with Functional Interfaces

In Java 8, you can use lambda expressions to handle exceptions within custom functional interfaces.

Example: Functional Interface with Exception Handling

@FunctionalInterface
interface CheckedFunction {
    R apply(T t) throws Exception;
}

public class LambdaExceptionHandling {
    public static void main(String[] args) {
        CheckedFunction parse = s -> Integer.parseInt(s);
        try {
            System.out.println(parse.apply("123")); // Output: 123
            System.out.println(parse.apply("abc")); // Throws NumberFormatException
        } catch (Exception e) {
            System.out.println("Exception: " + e.getMessage());
        }
    }
}

This allows for cleaner and reusable handling of methods that may throw exceptions, especially when working with streams or APIs.

2. Exception Handling in Streams

Streams in Java 8 don’t allow checked exceptions directly inside lambda expressions. To handle them, you can:

a. Wrap checked exceptions in a RuntimeException

List list = Arrays.asList("10", "20", "abc");

list.stream().forEach(item -> {
    try {
        int number = Integer.parseInt(item);
        System.out.println(number);
    } catch (NumberFormatException e) {
        System.out.println("Invalid number: " + item);
    }
});

b. Use a utility wrapper for lambdas that throw exceptions

public static  Consumer throwSafe(CheckedFunction function) {
    return i -> {
        try {
            function.apply(i);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

This is particularly useful when mapping over streams with methods that throw checked exceptions.

3. Optional for Handling NullPointerException

Java 8 introduced Optional as a container object which may or may not contain a non-null value. This helps to avoid NullPointerException, one of the most common unchecked exceptions.

Example:

public class OptionalExample {
    public static void main(String[] args) {
        String name = null;
        Optional optionalName = Optional.ofNullable(name);

        System.out.println(optionalName.orElse("Default Name")); // Output: Default Name
    }
}

Instead of explicitly checking for null and throwing a NullPointerException, Optional provides a cleaner and safer approach.

4. CompletableFuture and Exception Handling in Async Code

Java 8 introduced CompletableFuture for writing asynchronous, non-blocking code with support for exception handling.

Example:

import java.util.concurrent.*;

public class FutureExceptionHandling {
    public static void main(String[] args) {
        CompletableFuture future = CompletableFuture.supplyAsync(() -> {
            if (true) {
                throw new RuntimeException("Something went wrong!");
            }
            return "Success";
        }).exceptionally(ex -> {
            System.out.println("Handled: " + ex.getMessage());
            return "Recovered";
        });

        System.out.println(future.join()); // Output: Handled: Something went wrong! Recovered
    }
}

This approach is clean, and it keeps exception handling separate from the main logic, especially for async tasks.

15. Common Built-in Exceptions

  • ArithmeticException
  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • ClassNotFoundException
  • IOException
  • NumberFormatException

16. Best Practices in Exception Handling

  • Catch specific exceptions first.
  • Avoid catching generic Exception unless necessary.
  • Never ignore exceptions (e.g., empty catch blocks).
  • Always clean up resources in finally or use try-with-resources.
  • Throw custom exceptions with meaningful names and messages.

17. Real-World Analogy for Exception Handling

Imagine withdrawing money from an ATM. If the network fails or your card is invalid, the machine shows a proper error message and doesn’t crash. This is similar to how Java handles exceptions.

18. Advantages of Exception Handling

  • Separates error-handling code from regular logic.
  • Promotes graceful application termination.
  • Enhances code robustness and reliability.

19. Disadvantages of Improper Handling

  • Swallowing exceptions hides bugs.
  • Generic catch blocks may mask important details.
  • Overuse of try-catch can clutter code.

 

Conclusion

Exception handling in Java is a crucial mechanism that ensures your programs can deal with unexpected situations gracefully without crashing. By using constructs like try, catch, finally, throw, and throws, Java allows developers to anticipate errors, handle them properly, and maintain the normal flow of execution.

Understanding the types of exceptions (checked, unchecked, and errors), the exception hierarchy, and how to use try-catch-finally blocks helps in writing robust, maintainable, and user-friendly applications.
It not only improves the user experience by providing meaningful error messages but also enhances the stability and reliability of your code.

In short, exception handling is not just about fixing problems — it's about writing programs that expect the unexpected and respond to it with confidence and control.

 

Next Blog- Complete Guide to Strings in Java

Sanjiv
0

You must logged in to post comments.

Get In Touch

123 Street, New York, USA

+012 345 67890

techiefreak87@gmail.com

© Design & Developed by HW Infotech