Unit 2: Chapter 1
Exception Handling
Summary of Exception Handling in Java
#### Overview
- Purpose: Java exceptions are objects that manage errors during the execution of a Java program,
improving robustness by handling abnormal conditions gracefully.
#### Key Points
1. Exception vs Error:
- Exceptions are manageable within the application, e.g., `NullPointerException`.
- Errors are severe issues outside the application control, e.g., `OutOfMemoryError`.
2. Types of Exceptions:
- Checked Exceptions: Checked at compile-time; must be caught or declared.
- Unchecked Exceptions: Runtime exceptions; don't need to be explicitly handled.
- Errors: Indicate serious issues, typically not caught because they are fatal.
3. Exception Handling Mechanisms:
- try-catch Blocks: Enclose code that might throw exceptions and handle them if they occur.
- finally Block: Executes code regardless of an exception's occurrence, usually for resource cleanup.
- throw Keyword: Used to explicitly throw an exception.
- throws Keyword: Declares that a method can throw exceptions, which must be handled by the
caller.
#### JVM and Exceptions
- Exception Handling Flow:
- Exceptions thrown by the JVM or explicitly by code.
- Searched for corresponding catch blocks to handle the exception.
- If uncaught, exceptions are propagated up the call stack.
- `finally` blocks executed for cleanup.
- JVM may terminate the program if exceptions are unhandled.
#### Practical Application
- Use in Code: Incorporate try-catch to manage potential errors and finally for resource management.
- Custom Exceptions: Create user-defined exceptions for specific needs beyond Java's built-in
exceptions.
This condensed summary captures the core concepts of exception handling in Java, focusing on the
distinction between errors and exceptions, the types of exceptions, and the mechanisms to handle
them effectively.
Detailed notes of Exception Handling in Java
Exception Handling in JAVA
Java exceptions are objects that represent an abnormal condition or error encountered during the
execution of a Java program. They provide a way to handle and propagate errors in a structured
manner. Exceptions can occur due to various reasons, such as invalid input, resource unavailability,
or programming errors.
Java exceptions are important for several reasons:
1. Error Handling: Exceptions provide a mechanism to handle errors gracefully rather than allowing
them to crash the program. This helps improve the robustness and reliability of the software.
2. Separation of Concerns: By separating error-handling code from normal program logic, exceptions
promote cleaner and more modular code. This makes it easier to understand and maintain the
codebase.
3. Propagation: Exceptions can be propagated up the call stack until they are caught and handled by
an appropriate catch block. This allows errors to be handled at higher levels of the program where
more context is available.
4. Debugging: Exceptions provide valuable information about the cause and location of errors, which
aids in debugging and troubleshooting issues in the code.
5. Resource Management: Exceptions are commonly used for resource management, such as closing
files, database connections, or network sockets, even in the presence of errors.
Overall, Java exceptions play a crucial role in building robust and reliable software by providing a
structured way to handle errors and abnormal conditions during program execution. They help
improve the maintainability, reliability, and debuggability of Java applications.
Exceptions vs Error:
Exceptions and errors are both types of abnormal conditions that can occur during the execution of
a Java program, but they differ in their nature and how they should be handled:
1. Exceptions:
- Exceptions represent abnormal conditions that can be handled within the application.
- They are objects derived from the `Throwable` class or one of its subclasses, such as
`RuntimeException` or `IOException`.
- Examples include `NullPointerException`, `ArrayIndexOutOfBoundsException`, and
`FileNotFoundException`.
- Exceptions can be caught and handled using try-catch blocks or propagated up the call stack until
they are caught by an appropriate catch block.
- Handling exceptions allows the program to recover from errors and continue executing without
crashing.
2. Errors:
- Errors represent abnormal conditions that are generally beyond the control of the application and
cannot be easily recovered from.
- They are objects derived from the `Error` class or its subclasses, such as `OutOfMemoryError` or
`StackOverflowError`.
- Examples include `OutOfMemoryError`, `StackOverflowError`, and `AssertionError`.
- Errors typically indicate serious problems that may require intervention at the system level rather
than within the application.
- Unlike exceptions, errors are not typically caught and handled by the application because
attempting to do so may not be meaningful or practical.
- Errors are usually fatal and may lead to termination of the Java Virtual Machine (JVM) or the
application itself.
In summary, exceptions are used to represent recoverable abnormal conditions within the
application, while errors are used to represent severe or unrecoverable abnormal conditions that
may require intervention at the system level. Handling exceptions allows the program to
gracefully recover from errors and continue executing, while errors usually indicate serious
problems that may lead to termination of the program.
Types of Exceptions
In Java, exceptions are categorized into two main types: checked exceptions and unchecked
exceptions. Additionally, there is a third type called errors. Here's an overview of each type:
1. Checked Exceptions:
- Checked exceptions are exceptions that are checked at compile-time by the compiler.
- They are subclasses of `Exception` (but not `RuntimeException`) and must be either caught and
handled or declared in the method signature using the `throws` clause.
- Examples include `IOException`, `FileNotFoundException`, and `SQLException`.
- Checked exceptions typically represent conditions that are outside the control of the program,
such as I/O errors or network failures.
2. Unchecked Exceptions:
- Unchecked exceptions, also known as runtime exceptions, are exceptions that are not checked at
compile-time.
- They are subclasses of `RuntimeException` and its subclasses.
- Unchecked exceptions do not need to be caught or declared in the method signature, making
them less restrictive.
- Examples include `NullPointerException`, `ArrayIndexOutOfBoundsException`, and
`ArithmeticException`.
- Unchecked exceptions usually represent programming errors or logic errors that could have been
avoided with proper coding practices.
3. Errors:
- Errors are exceptional conditions that are typically beyond the control of the application and may
indicate serious problems with the JVM or the underlying system.
- They are subclasses of `Error` and its subclasses.
- Errors are not typically caught or handled by the application because attempting to do so may not
be meaningful or practical.
- Examples include `OutOfMemoryError`, `StackOverflowError`, and `AssertionError`.
- Errors usually indicate severe problems that may lead to termination of the JVM or the
application.
In summary, Java exceptions are categorized into checked exceptions, unchecked exceptions, and
errors. Checked exceptions must be handled or declared, while unchecked exceptions and errors do
not need to be handled or declared. Understanding the different types of exceptions helps in
designing robust and reliable Java applications.
Fig: Types of Exceptions
Control flow in Exception
Control flow in exceptions refers to how the program's execution is altered when an exception is
thrown and caught. Here's an explanation of the control flow in Java exceptions:
1. Throwing an Exception:
- When an exceptional condition occurs during the execution of a Java program, an exception
object is created and thrown using the `throw` keyword.
- The thrown exception can be a predefined exception class provided by Java, such as
`NullPointerException`, or a custom exception class defined by the programmer.
2. Catching an Exception:
- When an exception is thrown, the JVM searches for a corresponding `catch` block that can handle
the exception.
- A `catch` block is associated with a try block and specifies the type of exception it can catch.
- If a matching `catch` block is found, the control flow jumps to that `catch` block, and the
exception is caught and handled.
3. Exception Handling:
- Inside the `catch` block, the programmer can write code to handle the exceptional condition.
- This may involve logging the error, displaying an error message to the user, or taking corrective
action to recover from the error.
4. Finally Block:
- Optionally, a `finally` block can be used to define code that should be executed regardless of
whether an exception is thrown or caught.
- The `finally` block is executed after the try block, and any matching `catch` blocks, regardless of
whether an exception occurred or not.
- This is useful for releasing resources or performing cleanup tasks, such as closing files or database
connections.
5. Propagating Exceptions:
- If an exception is thrown but not caught within the current method, it is propagated up the call
stack to the calling method.
- This process continues until a matching `catch` block is found, or until the exception reaches the
top-level of the program where it may cause the program to terminate.
Control flow in exceptions allows Java programs to handle abnormal conditions gracefully and
continue execution even in the presence of errors. By catching and handling exceptions,
programmers can ensure that their applications are robust and reliable.
JVM Reaction to Exceptions:
When the Java Virtual Machine (JVM) encounters an exception during the execution of a Java
program, it reacts in a specific manner. Here's an explanation of the typical reaction of the JVM to
exceptions:
1. Exception Thrown:
- When an exceptional condition occurs during the execution of a Java program, an exception
object is created to represent the exceptional condition.
- This can happen due to various reasons such as invalid input, runtime errors, or unexpected
conditions.
2. Exception Propagation:
- The JVM searches for an appropriate exception handler to handle the exception.
- It begins by looking for a `try` block that encloses the code where the exception occurred.
3. Searching for Exception Handler:
- If a matching `catch` block is found within the same method or in any calling methods up the call
stack, the control flow is transferred to that `catch` block.
- The `catch` block contains code to handle the exceptional condition, such as logging the error,
displaying an error message, or taking corrective action to recover from the error.
4. Stack Unwinding:
- If the JVM reaches the top of the call stack (i.e., the main method) without finding a matching
`catch` block, the exception is propagated to the JVM's default exception handler.
- This default exception handler typically prints the stack trace, which shows the sequence of
method calls leading to the exception, and terminates the program.
5. Finally Block Execution:
- Before terminating the program, if there is a `finally` block associated with the `try` block that
caught the exception (or with any intervening `try` blocks), the code within the `finally` block is
executed.
- This ensures that any cleanup or resource release tasks specified in the `finally` block are
performed, regardless of whether an exception occurred.
6. Program Termination:
- If the exception is not caught and handled by any `catch` block, and there is no default exception
handler to handle it, the JVM terminates the program.
- The termination may be abrupt and may result in the program terminating unexpectedly.
In summary, the JVM reacts to exceptions by searching for an appropriate exception handler,
propagating the exception up the call stack if necessary, executing `finally` blocks, and terminating
the program if the exception is not caught and handled. Understanding how the JVM reacts to
exceptions is essential for writing robust and reliable Java programs.
Use of try, catch, finally, throw, throws in Exception Handling
Certainly! Let's go through each of the elements of exception handling in Java with explanations and
examples:
1. try: The `try` block encloses the code that may potentially throw an exception. It is followed by
one or more `catch` blocks or a `finally` block (or both).
2. catch: A `catch` block is used to catch and handle exceptions that occur within the corresponding
`try` block. It specifies the type of exception it can catch. You can have multiple `catch` blocks to
handle different types of exceptions.
3. finally: The `finally` block is used to define code that should be executed regardless of whether an
exception is thrown or caught. It is typically used for cleanup tasks such as releasing resources (e.g.,
closing files, database connections).
4. throw: The `throw` keyword is used to explicitly throw an exception within the code. It is typically
used when a certain condition is met, and the program cannot proceed further due to an error or
exceptional condition.
5. throws: The `throws` keyword is used in method declarations to indicate that the method may
throw certain types of exceptions. It specifies the exceptions that can be thrown by the method but
does not handle them itself. The calling method is responsible for handling the exceptions or
propagating them further.
Now, let's see how these elements are used in practice with an example:
import java.io.*;
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
// Code that may potentially throw an exception
FileReader fileReader = new FileReader("nonexistent-file.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = bufferedReader.readLine();
System.out.println("First line of the file: " + line);
} catch (FileNotFoundException e) {
// Handling specific exception type
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
// Handling another specific exception type
System.out.println("Error reading file: " + e.getMessage());
} finally {
// Cleanup code that always executes, regardless of whether an exception occurred
System.out.println("Finally block executed");
try {
// Close resources in finally block
bufferedReader.close();
} catch (IOException e) {
System.out.println("Error closing file: " + e.getMessage());
}
}
// Explicitly throw an exception
int x = 10;
int y = 0;
try {
if (y == 0) {
throw new ArithmeticException("Division by zero");
}
int result = x / y;
System.out.println("Result of division: " + result);
} catch (ArithmeticException e) {
// Handling explicitly thrown exception
System.out.println("Error: " + e.getMessage());
}
}
}
Explanation:
- In the first `try` block, we attempt to read the first line of a file. If the file does not exist
(`FileNotFoundException`) or an error occurs while reading the file (`IOException`), the
corresponding `catch` blocks handle these exceptions.
- The `finally` block is used to close the `BufferedReader` resource, ensuring that it is always closed
regardless of whether an exception occurs.
- The second `try` block demonstrates the use of `throw` to explicitly throw an `ArithmeticException`
when attempting to divide by zero. This exception is caught and handled by the corresponding
`catch` block.
- The `finally` block is also executed in this case, demonstrating that `finally` blocks are always
executed, even if an exception is thrown or caught.
This example illustrates the use of `try`, `catch`, `finally`, `throw`, and `throws` in exception handling
in Java. It shows how to handle exceptions, perform cleanup tasks, and explicitly throw exceptions
when necessary.
In-built and User Defined Exception
In Java, exceptions can be broadly categorized into two types: in-built exceptions and user-defined
exceptions. Here's an explanation of each type with examples:
1. In-built Exceptions:
- In-built exceptions are predefined exception classes provided by Java.
- They are part of the Java API and are typically used to handle common exceptional conditions
encountered in Java programs.
- Examples of in-built exceptions include `NullPointerException`, `FileNotFoundException`,
`ArithmeticException`, `ArrayIndexOutOfBoundsException`, etc.
Example:
public class InBuiltExceptionExample {
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
int element = arr[4]; // Accessing index out of bounds
System.out.println("Element at index 4: " + element);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Array index out of bounds");
}
}
}
Explanation:
- In this example, we attempt to access an element at index 4 in an array of size 3, which results in
an `ArrayIndexOutOfBoundsException`.
- We catch and handle this exception using a `catch` block that specifically catches
`ArrayIndexOutOfBoundsException`.
2. User-defined Exceptions:
- User-defined exceptions are custom exception classes defined by the programmer to represent
specific exceptional conditions in their application domain.
- They are subclasses of the `Exception` class or one of its subclasses.
- User-defined exceptions allow developers to define and handle application-specific exceptional
conditions in a structured manner.
Example:
// User-defined exception class
class InvalidAgeException extends Exception {
InvalidAgeException(String message) {
super(message);
}
}
public class UserDefinedExceptionExample {
public static void validateAge(int age) throws InvalidAgeException {
if (age < 0 || age > 120) {
throw new InvalidAgeException("Invalid age: " + age);
} else {
System.out.println("Valid age: " + age);
}
}
public static void main(String[] args) {
try {
validateAge(150); // Calling method with invalid age
} catch (InvalidAgeException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Explanation:
- In this example, we define a user-defined exception class `InvalidAgeException` that extends the
`Exception` class.
- We define a method `validateAge` that checks if the given age is within a valid range (0 to 120). If
not, it throws an `InvalidAgeException`.
- In the `main` method, we call the `validateAge` method with an invalid age (150), which results in
an `InvalidAgeException`.
- We catch and handle this user-defined exception using a `catch` block.
In summary, in-built exceptions are predefined exception classes provided by Java to handle
common exceptional conditions, while user-defined exceptions are custom exception classes
defined by the programmer to handle application-specific exceptional conditions. Both types of
exceptions allow developers to handle errors and exceptional conditions in their Java programs in a
structured manner.