No matter how carefully a program is written, unexpected situations can still occur during execution. A user might enter text where a number was expected, a file the program tries to open might not exist, or a calculation might attempt to divide by zero. Without a proper way to handle these situations, such errors would cause the entire program to crash abruptly.
Java addresses this challenge through a structured approach called exception handling, which allows a program to detect problems as they occur and respond to them gracefully, rather than terminating unexpectedly. This makes applications more reliable and provides a better experience for the people using them.
In this tutorial, you will learn what exceptions are, how the try-catch-finally structure works, the difference between checked and unchecked exceptions, how to use the throw and throws keywords, and how to create your own custom exceptions.
An exception is an event that disrupts the normal flow of a program's execution, typically caused by an error condition that occurs while the program is running. When an exception occurs and is not handled, the program stops executing and displays an error message describing what went wrong.
public class UnhandledExceptionExample {
public static void main(String[] args) {
int[] weeklyViews = {1200, 1500, 1800};
System.out.println("CS Engineering Gyan views: " + weeklyViews[5]);
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
In this example, attempting to access an index that does not exist in the array causes the program to terminate immediately with an error message, since there is no mechanism in place to handle this unexpected situation.
Java allows you to anticipate potential problems by placing risky code inside a try block, and defining how to respond to specific errors inside one or more catch blocks. If an exception occurs inside the try block, control immediately jumps to the matching catch block instead of crashing the program.
try {
// code that might cause an exception
} catch (ExceptionType e) {
// code to handle the exception
}
public class TryCatchExample {
public static void main(String[] args) {
int[] weeklyViews = {1200, 1500, 1800};
try {
System.out.println("CS Engineering Gyan views: " + weeklyViews[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("CS Engineering Gyan: Requested data is not available for that day.");
}
}
}
CS Engineering Gyan: Requested data is not available for that day.
Instead of the program crashing, the catch block intercepts the exception and displays a friendly, informative message, allowing the rest of the program to continue running normally afterward.
The finally block contains code that always executes, regardless of whether an exception occurred or not. It is commonly used for cleanup tasks, such as closing files or releasing resources, that must happen no matter what.
try {
// risky code
} catch (ExceptionType e) {
// handling code
} finally {
// code that always runs
}
public class FinallyExample {
public static void main(String[] args) {
try {
int result = 100 / 0;
System.out.println(result);
} catch (ArithmeticException e) {
System.out.println("CS Engineering Gyan: Cannot divide by zero.");
} finally {
System.out.println("CS Engineering Gyan: Calculation attempt completed.");
}
}
}
CS Engineering Gyan: Cannot divide by zero. CS Engineering Gyan: Calculation attempt completed.
Notice that the message inside the finally block is printed regardless of the exception, demonstrating that this block runs whether the try block succeeds or an exception is caught.
A single try block can be followed by multiple catch blocks, allowing a program to handle different types of exceptions in different ways, depending on what specifically went wrong.
public class MultipleCatchExample {
public static void main(String[] args) {
try {
int[] subscriberData = {50000, 62000, 71000};
System.out.println("CS Engineering Gyan subscribers: " + subscriberData[4]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("CS Engineering Gyan: Subscriber record not found.");
} catch (Exception e) {
System.out.println("CS Engineering Gyan: An unexpected error occurred.");
}
}
}
CS Engineering Gyan: Subscriber record not found.
Java checks each catch block in order and executes the first one that matches the type of exception thrown. Placing the more general Exception catch block after specific ones ensures that specific exceptions are handled precisely, while unexpected ones are still caught safely.
Java classifies exceptions into two broad categories, based on whether the compiler forces you to handle them explicitly or not.
| Checked Exceptions | Unchecked Exceptions |
|---|---|
| Checked by the compiler during compilation. | Not checked by the compiler, occurring during program execution. |
| Must be either caught or declared using the throws keyword. | Handling them is optional, though recommended for reliability. |
| Examples include IOException and SQLException. | Examples include ArithmeticException and NullPointerException. |
Understanding this distinction helps explain why some exceptions must be explicitly handled in your code, while others can technically occur without the compiler forcing you to prepare for them in advance.
The throw keyword is used to manually trigger an exception within your code, typically when a specific condition indicates that something invalid has occurred, even if Java itself would not have thrown an exception automatically.
public class ThrowExample {
static void checkSubscriberCount(int count) {
if (count < 0) {
throw new IllegalArgumentException("Subscriber count cannot be negative.");
}
System.out.println("CS Engineering Gyan subscriber count: " + count);
}
public static void main(String[] args) {
checkSubscriberCount(-500);
}
}
Exception in thread "main" java.lang.IllegalArgumentException: Subscriber count cannot be negative.
Since this exception is not caught anywhere in the example, the program terminates with the custom error message. In real applications, this kind of thrown exception would typically be caught and handled using a try-catch block.
The throws keyword is used in a method declaration to indicate that the method might throw a particular checked exception, passing the responsibility of handling it to whichever code calls the method.
import java.io.IOException;
public class ThrowsExample {
static void readChannelData() throws IOException {
throw new IOException("Unable to read CS Engineering Gyan data file.");
}
public static void main(String[] args) {
try {
readChannelData();
} catch (IOException e) {
System.out.println("Error occurred: " + e.getMessage());
}
}
}
Error occurred: Unable to read CS Engineering Gyan data file.
Here, the method readChannelData declares that it might throw an IOException using the throws keyword, requiring the calling code in the main method to handle that possibility using a try-catch block.
| throw | throws |
|---|---|
| Used to actually trigger an exception at a specific point in the code. | Used in a method signature to declare that an exception might occur. |
| Followed by a single exception object. | Can list multiple exception types, separated by commas. |
| Used inside a method body. | Used alongside a method declaration. |
Java also allows developers to define their own exception classes, which is useful when built-in exception types do not accurately describe the specific problem occurring in an application.
class InvalidVideoLengthException extends Exception {
InvalidVideoLengthException(String message) {
super(message);
}
}
public class CustomExceptionExample {
static void checkVideoLength(int minutes) throws InvalidVideoLengthException {
if (minutes <= 0) {
throw new InvalidVideoLengthException("Video length must be greater than zero minutes.");
}
System.out.println("CS Engineering Gyan video length: " + minutes + " minutes");
}
public static void main(String[] args) {
try {
checkVideoLength(-10);
} catch (InvalidVideoLengthException e) {
System.out.println("CS Engineering Gyan error: " + e.getMessage());
}
}
}
CS Engineering Gyan error: Video length must be greater than zero minutes.
By extending the built-in Exception class, InvalidVideoLengthException becomes a fully functional custom exception, complete with its own descriptive error message that clearly reflects the specific problem being reported.
| Exception | Common Cause |
|---|---|
| ArithmeticException | Occurs during invalid mathematical operations, such as division by zero. |
| ArrayIndexOutOfBoundsException | Occurs when accessing an array index that does not exist. |
| NullPointerException | Occurs when trying to use an object reference that has not been assigned a value. |
| NumberFormatException | Occurs when attempting to convert an invalid string into a numeric type. |
| ClassCastException | Occurs when attempting an invalid type conversion between objects. |
| Mistake | Correct Practice |
|---|---|
| Catching a general Exception when a specific exception type would be clearer. | Catch the most specific exception type relevant to the situation first. |
| Leaving a catch block completely empty. | Always include at least a message or logging statement inside a catch block. |
| Assuming the finally block will not run if an exception is caught. | Remember that finally always executes, whether or not an exception occurred. |
| Confusing throw and throws in method design. | Use throw to trigger an exception and throws to declare it in a method signature. |
Exception handling allows Java programs to detect and respond to unexpected problems in a controlled, predictable manner, rather than crashing abruptly. By using try, catch, and finally blocks together, developers can isolate risky code, define specific responses to different error types, and guarantee that essential cleanup tasks always run.
Understanding the difference between checked and unchecked exceptions, along with the proper use of throw and throws, gives you the tools needed to write more robust and reliable applications. Custom exceptions further extend this capability, allowing you to define error types that accurately reflect the specific needs of your own programs.
With a solid understanding of exception handling, you are now ready to explore the Collections Framework, which provides powerful tools for storing and managing groups of data in Java.