Exception handling in Java is typically a built-in mechanism that helps to maintain the stability and bug-free state of Java applications. In the practical aspect, you can encounter unexpected problems like invalid data, failures in accessing the file, or any database crashes that might stop the regular program operation. If these exceptions are left unhandled, they can cause sudden and unexpected crashes that will negatively impact the overall user experience and system stability
Java generally provides suitable methods to handle these kinds of problems. So with this Java Exception Handling tutorial, you are going to learn about exceptions and their types, exception hierarchies, and different kinds of techniques to ensure the stability and reliability of Java applications.
Table of Contents:
What are Exceptions in Java?
An exception in Java is an unexpected condition that generally arises whenever you execute a particular program that breaks the flow of statements. These conditions are typically triggered by different types of problems like logical errors, any invalid data entered by the user, resources unavailable, or any unexpected system behavior.
For example, when you try to divide the number by zero or you are accessing the array element which is not present in the actual array, these conditions will simply show the particular type of exception.
Importance of Exception Handling in Software Development
Exception handling is crucial in Java for several reasons:
- Prevents abrupt termination of the application: It simply ensures application operation is uninterrupted in case of an error.
- Improves debugging: It provides detailed error messages and stack traces in order to identify problems.
- Enhances maintainability: Exception handling also encourages structured methodology to handle failures without inundating code logic.
- Ensures reliability: It also helps to develop applications that can run smoothly and can handle any sudden system failures at run time.
Exception Hierarchy in Java
In Java, every exception is typically an instance of the root exception class called Throwable. The two primary subclasses of the Throwable class:
- Exception: Represents conditions that a program should handle.
- Error: Represents serious system-level issues that a program cannot recover from.
Here’s a visual representation of exception hierarchy in Java:
java.lang.Throwable
├── java.lang.Exception
│ ├── java.io.IOException
│ ├── java.sql.SQLException
│ ├── java.lang.RuntimeException
│ ├── java.lang.NullPointerException
│ ├── java.lang.ArithmeticException
│ ├── java.lang.ArrayIndexOutOfBoundsException
│
├── java.lang.Error
├── java.lang.OutOfMemoryError
├── java.lang.StackOverflowError

Key Exception Superclasses
1. Throwable (Root Class)
The Throwable is Java’s base exception structure. The printStackTrace(), getMessage(), and getCause() methods are included in it to diagnose exceptions.
2. Exception (Recoverable)
All checked and unchecked exceptions trace their roots to the Exception class.
- Checked Exceptions: Must be handled explicitly (i.e., IOException, SQLException).
- Unchecked Exceptions: Run-time errors caused by logical faults (i.e., NullPointerException, ArithmeticException).
3. Error (Non-Recoverable)
Errors occur by means of sudden system failures, such as crashes in, or overflow of the JVM, and cannot be handled by the application.
- Examples: OutOfMemoryError, StackOverflowError, VirtualMachineError.
Exception Hierarchy in Detail
Class |
Description |
Examples |
Throwable |
Root class for all exceptions and errors |
Exception, Error |
Exception |
Recoverable runtime issues |
IOException, SQLException |
RuntimeException |
Subclass of Exception for unchecked exceptions |
NullPointerException, ArrayIndexOutOfBoundsException |
Error |
Serious system issues that cannot be handled |
OutOfMemoryError, StackOverflowError |
Difference Between Exception and Error
In Java, Errors and Exceptions are subclasses of Throwable but for different reasons. Errors normally define serious issues for which recovery is normally not possible, while Exceptions define issues for which recovery is possible by the application.
Key Differences Between Exception and Error
Feature |
Exception |
Error |
Definition |
A recoverable condition that occurs during program execution. |
A serious system-level issue that is usually beyond the application’s control. |
Handling |
Can be handled using try-catch or propagated using throws. |
Cannot be handled programmatically; leads to application failure. |
Cause |
Caused by incorrect logic, invalid inputs, or external factors (e.g., file not found, invalid SQL queries). |
Caused by serious failures like memory leaks, JVM crashes, or hardware failures. |
Examples |
IOException, NullPointerException, SQLException |
OutOfMemoryError, StackOverflowError, VirtualMachineError |
Impact on Execution |
The program can continue executing after handling the exception. |
The program usually terminates abnormally. |
Example of an Exception (Recoverable)
An exception might be handled, and hence, the application is able to continue running:
Output:

Here, the application does not crash since the exception is handled properly.
Example of an Error (Non-Recoverable)
An error typically causes program termination:
Output:

Types of Exceptions in Java
Java exceptions have three types: checked, unchecked, and errors, depending on their treatment and their impact on the application. These types help in writing bug-free and strong applications.
1. Checked Exceptions
Checked exceptions are exception classes, and they must be handled during compile-time either by declaring using throw or by using try-catch. If left unhandled, the compilation gets interrupted. Checked exceptions usually result in a response to external factors such as access to files or to databases.
Examples of Checked Exceptions:
- IOException: It occurs in case of any issue in a file operation.
- SQLException: In case of any exception in database access.
- ClassNotFoundException: If a required class is missing in the classpath
Example: Handling a Checked Exception
Output:

Here, FileNotFoundException is checked in compile-time, and therefore, is forced to handle.
2. Unchecked Exceptions
Unchecked exceptions only occur during the runtime if there is any logical bug in the code. These do not always have to be handled, but their handling stabilizes programs.
Examples of Unchecked Exceptions
- NullPointerException: Thrown when accessing a null reference.
- ArithmeticException: Raised while dividing by zero.
- ArrayIndexOutOfBoundsException: Raised while trying to access an array member using an invalid index.
Example: Unchecked Exception (NullPointerException)
Output:

Here, the NullPointerException is happening simply because we’re calling on a null reference..
3. Errors
Errors are basically not exceptions but system resources or JVM serious issues. These cannot be recovered and usually result in the termination of the application.
Examples of Errors:
- OutOfMemoryError: If the JVM is running low on memory.
- StackOverflowError: These result from deep recursion or infinite loops.
- VirtualMachineError: Refers to a serious issue in the JVM.
Example: StackOverflowError
Output:

This example demonstrates StackOverflowError, where a function is calling itself infinitely.
Exception Keywords in Java
Keyword |
Description |
try |
Defines a block of code to be tested for exceptions. |
catch |
Catches and handles exceptions thrown inside a try block. |
finally |
Executes a block of code after try-catch, regardless of an exception. |
throw |
Used to manually throw an exception. |
throws |
Declares exceptions a method can throw. |
How JVM Handles Exceptions in Java
Java generally provides a built-in exception handling mechanism in order to ensure the program will run smoothly without any bugs. If any exception is then found during the program execution, the JVM(Java Virtual Machine) simply goes with the specified procedure to handle that exception. If the exception is left unchecked, the JVM will simply close the application and give the exception message.
Steps in JVM Exception Handling
- Exception Occurs: First, an exception generally occurs by any programming mistakes like dividing by zero or accessing a null reference.
- JVM Creates an Exception Object: Then the exception object for the corresponding exception type (i.e., ArithmeticException, NullPointerException, etc.) is created by the JVM.
- JVM Searches for Matching Catch Block: The JVM then looks for a try-catch block statement to execute in case of an exception.
- If a Handler is Encountered: The code in that particular catch block is then executed, and the application continues to run.
- If No Handler is Present: The exception continues to propagate up the calling chain. If no method handles it, the application is shut down by the JVM and prints a stack trace.
Practical Example for Exception Handling Mechanism in JVM
1. Exception Thrown by a Method
When any exception occurs in the code, it is then passed to the runtime system of the JVM, where the JVM basically searches for the particular exception handler.
Output:

Here, the divide(10, 0) method typically throws an ArithmeticException. As there is no exception handling, the exception is propagated to the JVM, and the program terminates and prints the stack trace.
2. Exception Propagation in JVM
If an exception is not processed in the current method, the JVM propagates it up through the calling chain until it finds a handler. If no such handler is found, the application terminates.
Example: Uncaught Exception Propagation
Output:

Here, method2() throws an ArithmeticException, but no method handles it, and therefore, the exception proceeds to method1(), and eventually to main(), and exits.
3. Default Exception Handling by JVM
When the particular exception is not handled, the JVM typically performs default exception handling, i.e.,
- Prints the exception type (java.lang.ArithmeticException)
- Displays an error message (/ by zero)
- This shows the trace of the stack that includes the function calls, which simply leads to the exception
Example: Default Handling
Output:

The JVM terminates the application and prints diagnostic messages to find out where the bug is present.
How Developers Can Handle Exceptions in Java
For any program, exception handling in Java is very important to ensure the stability in programs and prevent unexpected crashes. Java typically provides various mechanisms for exception handling, and with them, programmers can program robust, maintainable, and bugless code.
1. Using try-catch Blocks
Step 1: Learn What is try-catch block
- The try block contains code in which the condition may throw an exception.
- The catch block simply captures the exception and then handles it properly.
Step 2: Example Implementation
Output:

Step 3: Why Use try-catch?
- It prevents program crashes by simply catching the runtime errors.
- It also provides useful diagnostic information.
- It generally ensures the flow of the program continues after exception handling.
2. Using Multiple catch Blocks
Step 1: Why do you need to use multiple catch blocks?
A single try block might have multiple statements, and any of them might throw multiple types of exceptions. Java simply allows multiple catch blocks to handle them effectively.
Step 2: Example Implementation
Output:

Step 3: Best Practices for Catch Blocks
- Always Catch specific exception types before general exception types.
- Avoid catching the general Exception type except where necessary.
- You can also use multi-catch (catch (Exception1 | Exception2 e)) to handle exception classes.
3. Using the finally Block
Step 1: Understanding the finally block
- The finally block in Java will always get executed, even if there is an exception present or not
- It is mainly used for resource cleanups like in closing database connections and file streams.
Step 2: Example Implementation
Output:

Step 3: Why Use finally?
- You need to use the finally as it simply ensures that any necessary cleanup operation (closing files, releasing resources) is always executed.
- It also reduces the chances of resource leaks.
4. Using throws Keyword (Exception Propagation)
Step 1: Learn the throws keyword
- The throws keyword generally allows for a function to pass an exception to the calling code instead of catching and handling it.
- This method is generally called exception propagation.
Step 2: Example Implementation
Output:

Step 3: Why Use throws?
- You need to use the throws because it helps to propagate exceptions to upper levels in the calling chain.
- It also encourages cleaner code by keeping error-handling code separate from code in methods.
5. Creating Custom Exceptions
Step 1: Why Make Custom Exceptions?
- When built-in exceptions are not able to fulfill the requirements of the particular type of exceptions, you can define your own by inheriting Exception or RuntimeException.
Step 2: Example Implementation
Output:

Step 3: Benefits of Custom Exceptions
- Creating custom exceptions will simply provide you with meaningful error messages designed for business logic.
- It also improves code maintainability and code legibility.
6. Using Try-With-Resources (Automatic Resource Management)
Step 1: What is Try-With-Resources?
- This Try with resources concept was introduced in Java 7, and this feature closes such resources as database connections and file streams by default.
- It generally eliminates explicit use of a finally block for resource cleanup.
Step 2: Example Implementation
Output:

Step 3: Why Use Try-With-Resources?
- By using it, you can prevent resource leaks by keeping them closed.
- It also simplifies exception handling mechanisms and enhances code readability.
Advanced Topics in Exception Handling in Java
Exception handling in Java has many advanced concepts like exception propagation, chained exception, exception tuning, and many more. These advanced mechanisms generally help in designing scalable, robust, and high-performance applications.
1. Exception Propagation in Java
Exception propagation is where an exception moves up through the calling chain until either it is caught or causes a crash in the application.
How Does It Work?
- Whenever an exception is found in a particular method, Java typically searches for an exception-catching block in the method.
- If there is no such exception block, the exception will get passed on to the calling function.
- This whole process will continue until either exception is caught or propagates to the JVM, and thus terminates abnormally.
Example of Exception Propagation:
Output:

Key Takeaways
- If there is an exception that is left unhandled in a particular method, it is passed up to calling contexts.
- If no exception in the call chain is able to handle the exception, the JVM ends the application.
- You can use throws to declare that a method may throw exceptions.
2. Chained Exceptions in Java
Chained exceptions offer programmers the capability to chain an exception to another, keeping the root exception of an exception.
Why Use Chained Exceptions?
- These chained exceptions generally help to track the root exception to an upper-level exception.
- It is very useful when an exception is triggered by something else, for instance, hiding low-level exceptions in high-level exceptions.
Example of Chained Exceptions
Output:

Key Takeaways
- Chained exceptions retain the full context of an exception.
- Always use getCause() to retrieve the root exception.
- It is mainly preferred for encapsulating low-level exceptions in business-oriented exceptions.
3. Impact of Exceptions on Performance and Optimization
3.1 Performance Considerations
- Throwing exceptions is very costly, for the JVM captures trace details on the stack.
- Frequent exceptions typically slow down the application, mainly in performance-critical systems.
3.2 Best Practices for Exception Optimization
- Avoid Using Exceptions for Control Flow
- Instead of throwing exceptions for standard conditions, use conditional checks.
if (list.isEmpty()) {
System.out.println("List is empty");
} else {
System.out.println(list.get(0));
}
- Use Specific Exceptions Instead of Generic
- Catching Exception or Throwable might mask real problems and affect debugging
- Minimize Stack Trace Logging in High-Throughput Applications
- Logging full stack traces is costly in high-performance applications.
4. Exception Handling in Streams and Lambdas
Java 8 introduced the functional programming that generally supports concepts like lambdas and streams, and in these concepts, the approach to handling exceptions should be different.
Problem: Checked Exceptions in Streams
Streams and lambdas do not support checked exceptions directly.
List names = List.of("Ayaan", "Alam", "Abhinav");
names.forEach(name -> {
Thread.sleep(1000); // Compile-time error: InterruptedException
});
Solution: Wrapping Exceptions in RuntimeException
One way to handle checked exceptions in lambdas is to enclose them in unchecked exceptions.
Output:

Best Practices for Exception Handling in Streams
- You need to use try-catch in lambdas where exception handling at the lambda level is necessary.
- Create a wrapper function for exception
- Try to convert checked exceptions to unchecked if possible.
5. Exception Handling in Multithreaded Applications
5.1 Challenges in Multithreaded Exception Handling
- Each thread in these applications is independent, and thus, an exception in any thread does not affect any other.
- Exceptions inside a particular thread do not affect the parent thread.
5.2 Solution: Using UncaughtExceptionHandler
Java provides Thread.UncaughtExceptionHandler to deal with unhandled thread exceptions.
Example: Handling Exceptions in Threads
Output:

Key Takeaways
- Always use the Thread.UncaughtExceptionHandler to catch the thread exceptions.
- Consider using ExecutorService to manage thread exceptions.
- Also, handle exceptions on the thread level to avoid quiet failures.
Real-world Scenarios
1. Enterprise Applications
In large-scale business systems, there might be exceptions in various vital areas:
- Database Operations:
- At the end of connecting to a database, SQLExceptions can occur if the connection is invalid or if there is an invalid query.
- Handling these kinds of exceptions assures that the application is capable of logging the exception, re-executing the operation, or politely alerting the user rather than crashing the system.
- File Handling and I/O:
- Applications that read and/or write to files have to deal with potential issues such as files not found or access privileges (FileNotFoundException or IOException).
- Implementing proper try-catch blocks or using try-with-resources makes sure that file streams and resources are closed and released, whether an exception is produced or not.
2. Web Applications
Any web application ensures that it always provides a smooth user experience, even at the time of backend failures:
- REST API Exception Handling:
- When an invalid API endpoint either receives invalid data or contains faults in internal processing, a global exception handler is capable of catching exceptions and sending standardized error messages (like HTTP status code and JSON error messages).
- This helps to provide consistent client-side behavior and facilitates better debuggability.
- Session and Authentication Issues:
- Exceptions during user authentication, such as session timeouts or invalid tokens, require graceful handling to redirect users or provide appropriate error messages.
3. Distributed Systems and Microservices
- Remote Procedure Calls:
- In a microservice architecture, network delays or services becoming unavailable might cause exceptions on remote calls.
- Techniques like fallback and circuit breakers help to handle such exceptions to ensure system robustness.
- Concurrency and Multithreading:
- Exceptions occurring in independent threads (i.e., in a thread pool) should be processed using facilities such as Thread.UncaughtExceptionHandler to avoid quiet failures and provide sufficient logging.
4. Mobile and Desktop Applications
- User Input and UI Updates:
- Applications often have to validate user data and deal with exception messages from GUI components.
- Effective exception handling makes for better user experience through clear messages rather than an overall application crash.
- Resource Management:
- In environments where resources are limited, for example, on mobile, preventing exception propagation to cause resource leaks (such as open file handles or network socket leaks) is crucial.
Advantages of Exception Handling in Java
- Improves Code Maintainability: It simply splits error-handling code from business code, which keeps code cleaner and more maintainable.
- Enhances Program Trust: It prevents application crashes by catching multiple runtime errors.
- Ensures Resource Releasing: finally blocks and try-with-resources ensure releasing system resources that include the file streams and the database connections.
- Facilitates Debugging: Detailed exception stack traces help the developers to identify and fix issues efficiently.
Best Practices for Exception Handling in Java
- Catch Specific Exceptions: Don’t use general Exception or Throwable; handle only necessary exceptions for better clarity and for better debugging.
- Never Use Exceptions for Control Flow: Don’t replace conditionals (if-else) by using exceptions – it is slow and slows down system performance.
- Always Close in Finally or Use Try-With-Resources: Prevents system resource leaks, including file streams and database handles.
- Log Exceptions Appropriately: Utilize logging tools (Log4j, SLF4J) to capture exception details but omit sensitive data.
- Use Custom Exceptions If You Must: Make exception classes to specify distinct and definitive states of error in your application.
Conclusion
With this, you have come to the end of this Java Exception Handling Tutorial. Proper exception handling makes the Java applications robust, maintainable, and fault tolerant. By doing the exception handling, you can simply handle any kind of exception effectively. By learning advanced techniques like exception handling in streams, lambdas, and in multithreaded code, you can enhance the overall performance of advanced Java applications like web applications and distributed systems.
FAQs on Java Exception Handling
What is an Exception in Java?
An exception is something unexpected, such as dividing by zero or being unable to access a file, that breaks up program flow.
What is a Checked and Unchecked Exception in Java?
Checked exceptions (like IOException) must be handled in compile-time. Unchecked exceptions, such as NullPointerException, appear at runtime and do not have to be handled.
How does the throw keyword in Java compare to Java's throws?
The throw keyword is used to throw an exception directly in a function or code, whereas the throws keyword is used in the function’s signature to declare that the function is capable of throwing one or multiple exceptions.
What are good practices for exception handling in Java?
Best practices include catching exception types rather than catching Exceptions, releasing in a finally block or using try-with-resources, never using exception for flow control, and having good messages on exception throw.
What is the Difference between Final, Finally, and Finalize in Java?
final is reserved for constant declaration, to prevent method overriding, or for inheriting. finally is an exception handling block to execute code after a try-catch. finalize is a method called by the garbage collector before an object is collected, and may be overloaded to release system resources or to do cleanup.