Comprehensive Guide to Exception Handling in Java: Best Practices and Examples
Updated on Jan 07, 2025 | 15 min read | 7.3k views
Share:
For working professionals
For fresh graduates
More
Updated on Jan 07, 2025 | 15 min read | 7.3k views
Share:
Table of Contents
Did you know that Java powers over 9 million developers globally? Its reliability and scalability make it a favorite for everything from Android apps to complex financial systems. But what happens when things go wrong, like a file not found, invalid user input, or a network failure? That’s where exception handling in Java becomes crucial.
Exception handling allows you to anticipate, catch, and manage these errors, ensuring your application doesn’t crash and burn. Instead, it continues running smoothly, providing a better experience for users.
In this blog, you’ll learn how to master exception handling in Java with examples. From understanding the basics to implementing best practices, it will equip you with the tools to write resilient, error-tolerant code.
Let’s get started!
In Java, exceptions are disruptions that occur during the program's execution, preventing it from proceeding normally. These runtime issues don’t have to spell disaster. Instead, Java provides a structured framework for detecting and managing these errors.
By handling exceptions, you can ensure smooth program flow, provide meaningful error messages to users, and safeguard against crashes.
Let’s explore exception handling in Java in more detail!
Exceptions in Java are broadly categorized into two types: checked and unchecked exceptions. These categories help you determine which errors must be explicitly handled and which can occur without interrupting the program.
Have a look at the comparison table of both:
Aspect | Checked Exceptions | Unchecked Exceptions |
Definition | Exceptions checked at compile time. | Exceptions that occur at runtime. |
Examples | IOException, SQLException | ArithmeticException, NullPointerException |
Handling Requirement | Must be handled using try-catch or declared with throws. | No explicit handling required, but can be managed using try-catch. |
Occurrence | Often caused by external resources like files or databases. | Arises from logical errors in the code. |
To understand its implementation better, go through the examples given.
1. Checked Exception Example:
import java.io.FileReader;
import java.io.IOException;
public class CheckedExceptionExample {
public static void main(String[] args) {
try {
// Attempting to read a file that may not exist
FileReader file = new FileReader("nonexistentfile.txt");
} catch (IOException e) {
// Handling the IOException
System.out.println("Error: File not found!");
}
}
}
Output:
Error: File not found!
Code Explanation:
2. Unchecked Exception Example:
public class UncheckedExceptionExample {
public static void main(String[] args) {
// Attempting to divide by zero
int result = 10 / 0; // Triggers ArithmeticException
System.out.println("Result: " + result);
}
}
Output:
Exception in thread "main" java.lang.ArithmeticException: / by zero
Code Explanation:
Java’s exception system follows a clear hierarchy that starts with the Throwable class. This class has two main subclasses: Error and Exception. This structure helps you understand how to categorize and manage different types of errors.
Here’s an overview of the hierarchy of Java expectation classes:
Have a look at a code example demonstrating the hierarchy:
public class ExceptionHierarchyExample {
public static void main(String[] args) {
try {
// Attempting to access a null object
String str = null;
System.out.println(str.length()); // Triggers NullPointerException
} catch (NullPointerException e) {
System.out.println("Caught a NullPointerException!");
} catch (Exception e) {
System.out.println("Caught a general Exception!");
}
}
}
Output:
Caught a NullPointerException!
Explanation:
By understanding these fundamental concepts of exception handling in Java, you can write resilient, error-free applications.
If you wish to upskill and enhance these in-demand tech concepts, explore upGrad’s comprehensive software development programs from top universities!
Now, in any robust application, errors are inevitable — but how you handle them makes all the difference. Let’s see how you do it!
Java, one of the most developer-friendly languages, offers a structured exception-handling framework to tackle errors efficiently without disrupting the program flow. It ensures you’re not just catching errors but managing them intelligently.
Exception handling in Java includes key constructs like try-catch, finally, throw, and throws, which enable developers to handle errors effectively, combined with robust APIs and tools.
Let’s explore how these mechanisms work and how they prevent abrupt program failures.
1. try-catch: Controlled Error Resolution
Code Example:
public class TryCatchExample {
public static void main(String[] args) {
try {
int result = 10 / 0; // Triggers ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
}
}
}
Output:
Error: Cannot divide by zero!
In this code example of try-catch in Java, the program gracefully handles the exception instead of crashing and continues.
2. finally: Ensuring Cleanup
The finally block is always executed, regardless of whether an exception occurs, making it ideal for cleanup operations like closing files or releasing resources.
Code Example:
public class FinallyExample {
public static void main(String[] args) {
try {
int[] numbers = {1, 2};
System.out.println(numbers[5]); // Triggers ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Index out of bounds!");
} finally {
System.out.println("Execution complete.");
}
}
}
Output:
Error: Index out of bounds!
Execution complete.
In this example, the ‘finally’ block ensures that critical tasks are completed, even if an exception occurs.
3. throw: Manually Raising Exceptions
Code Example:
public class ThrowExample {
public static void main(String[] args) {
int age = 15;
if (age < 18) {
throw new IllegalArgumentException("Age must be 18 or older.");
}
}
}
Output:
Exception in thread "main" java.lang.IllegalArgumentException: Age must be 18 or older.
In the code snippet, when the condition age < 18 is met, the program raises an IllegalArgumentException.
4. throws: Declaring Exceptions
The throws keyword is used to declare the exceptions a method might throw, allowing the caller of the method to handle them.
Code Example:
public class ThrowsExample {
public static void checkNumber(int number) throws Exception {
if (number < 0) {
throw new Exception("Negative number not allowed!");
}
}
public static void main(String[] args) {
try {
checkNumber(-5);
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Output:
Error: Negative number not allowed!
Here, the throws keyword informs the caller that checkNumber might throw an exception, which must be handled explicitly in the calling method.
This structured way of exception handling in Java prevents programs from abruptly stopping by:
Also Read: File Handling in Java: How to Work with Java Files?
While Java’s built-in exceptions cover many standard error scenarios, some applications require custom error messages or specific behavior.
This is where custom exceptions come in. Let’s dive into it!
A custom exception allows you to create meaningful, application-specific error handling. For example, if you’re building a banking application, you might want to create exceptions like InsufficientFundsException or AccountNotFoundException to represent domain-specific errors.
Custom exceptions are useful when:
Now, let’s see how to create the custom exception step-by-step:
Step 1. Decide the Type of Custom Exception:
Step 2. Define the Exception Class:
Add constructors to initialize the exception with custom messages or additional data.
Step 3. Throw and Handle the Custom Exception:
Now that you have got the theoretical steps, let’s see the complete process implementation with code examples to help you understand better.
1. Define the Custom Exception Class
// Defining a custom exception
class InsufficientFundsException extends Exception {
// Constructor with a custom message
public InsufficientFundsException(String message) {
super(message); // Pass the message to the Exception class
}
}
2. Use the Custom Exception
public class CustomExceptionExample {
// Method to simulate withdrawing money
public static void withdraw(double amount, double balance) throws InsufficientFundsException {
if (amount > balance) {
// Throw the custom exception if funds are insufficient
throw new InsufficientFundsException("Insufficient funds: Unable to withdraw " + amount);
}
System.out.println("Withdrawal successful! Remaining balance: " + (balance - amount));
}
public static void main(String[] args) {
double balance = 500.0; // Initial account balance
try {
withdraw(600.0, balance); // Attempt to withdraw more than the balance
} catch (InsufficientFundsException e) {
// Handle the custom exception
System.out.println("Error: " + e.getMessage());
}
}
}
Output:
Error: Insufficient funds: Unable to withdraw 600.0
In the above code snippet:
Wish to build your foundational knowledge of Java? Start with upGrad’s core Java basics course and explore further!
Also Read: How to Code, Compile, and Run Java Projects
Exceptions are an integral part of programming, and as a Java developer, you’ll encounter various of them. Let’s understand them!
Understanding the most common exceptions and their causes is essential for debugging effectively and writing error-resilient code.
Let’s explore some of the frequently encountered exceptions in Java, their causes, and how they occur in everyday coding.
1. NullPointerException: Accessing Null References
This exception occurs when you attempt to access or modify an object that has not been initialized (i.e., is null).
How It Happens:
Code Example:
public class NullPointerExample {
public static void main(String[] args) {
String text = null; // Uninitialized string
System.out.println(text.length()); // Triggers NullPointerException
}
}
Output:
Exception in thread "main" java.lang.NullPointerException
How to Avoid: Always check for null before accessing objects, as shown in the below code:
if (text != null) {
System.out.println(text.length());
}
2. IOException: Input/Output Operation Failures
If something goes wrong, this exception is thrown during I/O operations, such as reading from or writing to a file.
How It Happens:
Code Example:
import java.io.FileReader;
import java.io.IOException;
public class IOExceptionExample {
public static void main(String[] args) {
try {
FileReader file = new FileReader("nonexistentfile.txt"); // File not found
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Output:
Error: nonexistentfile.txt (No such file or directory)
How to Avoid:
3. ArithmeticException: Invalid Arithmetic Operations
This exception is triggered by invalid arithmetic operations, such as dividing a number by zero.
How It Happens:
Code Example:
public class ArithmeticExample {
public static void main(String[] args) {
int result = 10 / 0; // Division by zero
System.out.println("Result: " + result);
}
}
Output:
Exception in thread "main" java.lang.ArithmeticException: / by zero
How to Avoid: Check denominators before performing division. See how it’s done below:
if (denominator != 0) {
System.out.println(10 / denominator);
} else {
System.out.println("Cannot divide by zero.");
}
Understanding these exceptions is the first step toward mastering exception handling in Java with examples!
Also Read: Top 32 Exception Handling Interview Questions and Answers [For Freshers & Experienced]
Next, let’s get into some advanced techniques for exception handling in Java with examples!
upGrad’s Exclusive Software Development Webinar for you –
SAAS Business – What is So Different?
Exception handling in Java goes beyond basic try-catch blocks. Advanced techniques enable developers to write cleaner, more efficient, context-aware, error-handling code.
Let’s explore these advanced concepts of exception handling in Java with examples.
Exception propagation is a mechanism in Java where an exception raised in one method moves up the call stack until a try-catch block catches it or terminates the program if unhandled.
When an exception occurs:
Have a look at the code example below:
public class ExceptionPropagationExample {
// Method that throws an exception
public static void methodA() {
throw new ArithmeticException("Division by zero in methodA");
}
// Method that calls methodA
public static void methodB() {
methodA(); // Exception propagates to methodB
}
public static void main(String[] args) {
try {
methodB(); // Exception propagates to main
} catch (ArithmeticException e) {
System.out.println("Caught Exception: " + e.getMessage());
}
}
}
Output:
Caught Exception: Division by zero in methodA
Code Explanation:
This shows how exceptions travel up the call stack, allowing centralized error handling.
Sometimes, one exception causes another. Chaining exceptions lets you link these related errors, preserving the full context for debugging.
This is particularly useful when you want to:
Have a look at the code example below:
public class ChainedExceptionExample {
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
System.out.println("Caught Exception: " + e.getMessage());
System.out.println("Caused by: " + e.getCause());
}
}
// Method that simulates an error and chains it
public static void methodA() throws Exception {
try {
int result = 10 / 0; // Triggers ArithmeticException
} catch (ArithmeticException e) {
throw new Exception("Error in methodA", e); // Chaining exception
}
}
}
Output:
Caught Exception: Error in methodA
Caused by: java.lang.ArithmeticException: / by zero
Code Explanation:
Managing resources like file streams or database connections can be tricky. If not closed properly, they can lead to resource leaks.
The try-with-resources statement, introduced in Java 7, automates resource management by automatically closing resources when no longer needed.
Have a look at the code example below:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesExample {
public static void main(String[] args) {
// Using try-with-resources for automatic resource management
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Print file content
}
} catch (IOException e) {
System.out.println("Error reading the file: " + e.getMessage());
}
}
}
Output (if file exists):
File content line 1
File content line 2
...
Output (if file doesn’t exist):
Error reading the file: example.txt (No such file or directory)
Code Explanation:
There you go! You must practice these concepts to take your exception handling in Java skills to the next level!
Also Read: Top 10 Java Books to Read to Improve Your Knowledge
Exception handling in Java is not just about catching errors; it’s about doing so in a way that ensures your application remains reliable, maintainable, and efficient. Following best practices helps you write clean and predictable code that is easier to debug.
Let’s explore key practices to make your exception handling robust and effective.
1. Catch Specific Exceptions
Always catch specific exceptions instead of using generic ones like Exception or Throwable. Specific handlers make debugging easier and provide clearer error messages.
Understand it with the code snippet below:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Arithmetic Error: Division by zero.");
}
2. Log Exceptions with Meaningful Messages
Use proper logging frameworks (e.g., Log4j, SLF4J) to log exceptions. Include meaningful messages and stack traces to provide a clear context for debugging.
See how it should be implemented:
import java.util.logging.Logger;
public class LoggingExample {
private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());
public static void main(String[] args) {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
logger.severe("Arithmetic error occurred: " + e.getMessage());
}
}
}
3. Avoid Using Exceptions for Control Flow
Exceptions are expensive in performance and should not replace regular conditional checks. Use proper logic to handle predictable conditions instead of relying on exceptions.
Example (What NOT to Do):
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // Avoid triggering exceptions deliberately
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Index out of bounds!"); // Poor control flow
}
Better Approach:
int[] numbers = {1, 2, 3};
if (5 < numbers.length) {
System.out.println(numbers[5]);
} else {
System.out.println("Index out of bounds!");
}
4. Use try-with-resources for Efficient Resource Management
Always use try-with-resources to manage resources like file streams and database connections. It ensures resources are closed automatically, even in the event of an exception.
Here’s how to do it:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResourcesBestPractice {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
}
Adopting these best practices will make your applications more resilient and maintainable, helping you become a better Java developer.
Also Read: Java Developer Salary: What to Expect in India 2025
Do you want to level up your Java skills and confidently tackle real-world programming challenges? Mastering core concepts like exception handling in Java is a game-changer for developers aiming to build robust, error-free applications.
upGrad specializes in bridging the gap between learning and career advancement. Their Java programs are tailored to equip you with foundational and advanced skills, ensuring you’re ready to tackle industry challenges head-on.
Some of the top courses include:
Take the first step today: book a career counseling session with upGrad experts, upskill, and excel in your future ahead!
Boost your career with our popular Software Engineering courses, offering hands-on training and expert guidance to turn you into a skilled software developer.
Master in-demand Software Development skills like coding, system design, DevOps, and agile methodologies to excel in today’s competitive tech industry.
Stay informed with our widely-read Software Development articles, covering everything from coding techniques to the latest advancements in software engineering.
References:
https://www.cogentinfo.com/resources/java-developer-demographics-and-statistics-in-the-us
Get Free Consultation
By submitting, I accept the T&C and
Privacy Policy
India’s #1 Tech University
Executive PG Certification in AI-Powered Full Stack Development
77%
seats filled
Top Resources