# Java Exception Handling

### **What is an Exception?**

In Java, an **exception** is an unexpected event or error that occurs during the execution of a program, disrupting its normal flow. Exceptions arise when the program encounters a condition it cannot handle, such as invalid user input, attempting to read a non-existent file, or dividing a number by zero.

When an exception occurs, Java provides a mechanism to detect, handle, and recover from these errors gracefully, rather than letting the program crash unexpectedly.

**Example of an Exception**:

```java
int result = 10 / 0; // Causes ArithmeticException: division by zero
```

If this exception is not handled, the program will terminate abruptly with an error message.

### **Errors vs. Exceptions**

Java delineates **errors** and **exceptions** based on severity and recoverability. **Errors** are critical, system-level malfunctions beyond a programmer's control, often caused by resource depletion or JVM failures, such as `OutOfMemoryError` when the JVM exhausts its memory pool or `StackOverflowError` due to unchecked recursion depth. These issues are typically irrecoverable and are not meant to be caught or managed by application logic. In contrast, **exceptions** signify recoverable disruptions stemming from invalid operations, user input errors, or flawed application logic. They are designed to be anticipated and handled gracefully, allowing the program to recover and proceed. Java enforces handling for **checked exceptions** like `IOException`, which arises during file operations, ensuring robust code that can cope with external failures. **Unchecked exceptions**, such as `NullPointerException` and `ArithmeticException`, stem from logical errors within the code and do not require mandatory handling. Proper management of exceptions, whether checked or unchecked, enhances the program's resilience and ensures graceful degradation rather than abrupt termination.

#### **Errors**

**Example**:

```java
public class StackOverflowDemo {
    public static void infiniteRecursion() {
        infiniteRecursion(); // Causes StackOverflowError
    }

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

#### **Exceptions**

**Example**:

```java
public class ExceptionDemo {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length()); // Causes NullPointerException
    }
}
```

### **Core Constructs**

#### 1\. `try` Block

* The `try` block contains code that may throw an exception during execution. It acts as a safety net, allowing you to enclose risky operations.
    

**Example**:

```java
try {
    int result = 10 / 0; // May cause ArithmeticException
}
```

#### 2\. `catch` Block

* The `catch` block handles exceptions thrown by the `try` block. It specifies what type of exception it can catch and provides a mechanism for recovery or logging.
    

**Example**:

```java
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Division by zero is not allowed.");
}
```

#### **Catching Superclass Exceptions**

* When a superclass exception (e.g., `Exception`) is caught before a subclass exception (e.g., `ArithmeticException`), the superclass block captures all exceptions of that type and its subclasses, preventing the subclass-specific block from executing.
    

**Example**:

```java
try {
    int result = 10 / 0;
} catch (Exception e) {
    System.out.println("Caught by superclass Exception.");
} catch (ArithmeticException e) {
    // This block will never be reached
    System.out.println("Caught ArithmeticException.");
}
```

**Explanation**:  
In this example, `Exception` is the superclass of `ArithmeticException`. Since the `Exception` block appears first, it captures all exceptions derived from `Exception`, including `ArithmeticException`. This prevents the more specific `ArithmeticException` block from executing.

#### 3\. `finally` Block

* The `finally` block contains code that **always executes** regardless of whether an exception is thrown or not. It is typically used for cleanup operations, such as closing files or releasing resources.
    

**Example**:

```java
try {
    FileReader reader = new FileReader("file.txt");
} catch (FileNotFoundException e) {
    System.out.println("File not found.");
} finally {
    System.out.println("Cleanup complete.");
}
```

**Explanation**:

* The `finally` block runs whether or not the `FileNotFoundException` occurs, ensuring that cleanup tasks are completed.
    

#### 4\. `throw` Keyword

* The `throw` keyword is used to explicitly raise an exception in your code. It can be used to signal that a method encountered an invalid condition.
    

**Example**:

```java
public void validateAge(int age) {
    if (age < 18) {
        throw new IllegalArgumentException("Age must be 18 or older.");
    }
}
```

**Explanation**:

* In this example, if `age` is less than 18, an `IllegalArgumentException` is explicitly thrown, stopping the normal execution of the method.
    

#### 5\. `throws` Keyword

* The `throws` keyword is used in a method signature to declare exceptions that the method might throw. This signals to the caller that they must handle or further declare the exception.
    

**Example**:

```java
public void readFile() throws IOException {
    FileReader reader = new FileReader("file.txt");
}
```

**Explanation**:

* The `readFile` method declares that it might throw an `IOException`. The calling method must either handle this exception with a `try-catch` block or declare it using `throws`.
    

### **Putting It All Together**

Here’s a complete example demonstrating all these constructs:

```java
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionHandlingDemo {
    public static void readFile(String filename) throws IOException {
        try {
            FileReader reader = new FileReader(filename);
            System.out.println("File opened successfully.");
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + e.getMessage());
        } finally {
            System.out.println("Execution of finally block: Closing resources.");
        }
    }

    public static void validateAge(int age) {
        if (age < 18) {
            throw new IllegalArgumentException("Age must be 18 or older.");
        }
        System.out.println("Age is valid.");
    }

    public static void main(String[] args) {
        try {
            readFile("example.txt");
            validateAge(16);
        } catch (IOException e) {
            System.out.println("IOException occurred: " + e.getMessage());
        } catch (IllegalArgumentException e) {
            System.out.println("Validation Error: " + e.getMessage());
        }
    }
}
```

**Explanation**:

1. `readFile`: Demonstrates the use of `try`, `catch`, `finally`, and `throws`.
    
2. `validateAge`: Shows how to use `throw` to explicitly raise an exception.
    
3. `main`: Calls both methods and handles exceptions using multiple `catch` blocks.
    

### **Key Takeaways**

* `try`: Contains code that may throw exceptions.
    
* `catch`: Handles exceptions thrown in the `try` block.
    
* **Superclass Catching**: Placing a superclass exception before a subclass in `catch` blocks will prevent the subclass handler from executing.
    
* `finally`: Executes cleanup code regardless of exceptions.
    
* `throw`: Explicitly raises an exception.
    
* `throws`: Declares exceptions that a method might throw.
    

## **Java Exception Class Hierarchy**

Java’s exception-handling system is structured around the `Throwable` class, which serves as the root of all errors and exceptions. The hierarchy enables Java programs to represent error conditions as objects and manage them through structured exception handling mechanisms.

The `Throwable` class branches into two primary subclasses: `Error` and `Exception`. The `Exception` class is further divided into **checked exceptions** and **unchecked exceptions** (runtime exceptions). This classification helps distinguish between severe system-level failures and recoverable application-level issues.

### **Hierarchy Overview**

```plaintext
Throwable
├── Error (Unchecked)
│   └── Examples: OutOfMemoryError, StackOverflowError
└── Exception
    ├── Checked Exceptions
    │   └── Examples: IOException, SQLException
    └── RuntimeException (Unchecked)
        └── Examples: NullPointerException, ArithmeticException
```

### **1.** `Error` Class

* **Definition**:  
    Represents severe system-level issues that are typically beyond the control of the application.
    
* **Nature**:  
    Errors indicate critical failures, such as hardware malfunctions or JVM-level problems. These are **unchecked** because they are not meant to be anticipated or handled by the application code.
    
* **Characteristics**:
    
    * Recovery is generally not possible or advisable.
        
    * Not meant to be caught or handled by regular application logic.
        
* **Common Examples**:
    
    * `OutOfMemoryError`: Indicates that the JVM has run out of memory.
        
    * `StackOverflowError`: Occurs when the call stack limit is exceeded due to deep recursion.
        

### **2.** `Exception` Class

* **Definition**:  
    Represents application-level issues that a program can anticipate, handle, and recover from.
    
* **Nature**:  
    Exceptions are divided into **checked exceptions** and **unchecked exceptions** based on whether the compiler enforces handling them.
    

#### **Checked Exceptions**

* **Definition**:  
    Exceptions that the compiler requires the programmer to handle explicitly, either by catching them or declaring them with the `throws` keyword.
    
* **Characteristics**:
    
    * Represent anticipated issues that are outside the program's direct control.
        
    * Examples include file I/O errors or database connection failures.
        
* **Common Examples**:
    
    * `IOException`: Occurs during input/output operations (e.g., reading a missing file).
        
    * `SQLException`: Indicates issues when accessing a database.
        

#### **Unchecked Exceptions (Runtime Exceptions)**

* **Definition**:  
    Exceptions that the compiler does **not** require to be explicitly handled.
    
* **Characteristics**:
    
    * Typically caused by programming logic errors.
        
    * These exceptions can be caught if desired, but handling is not mandatory.
        
* **Common Examples**:
    
    * `NullPointerException`: Occurs when accessing a `null` reference.
        
    * `ArithmeticException`: Occurs when performing illegal arithmetic operations, such as division by zero.
        

## **Checked vs. Unchecked Exceptions in Java**

Understanding the distinction between **checked** and **unchecked exceptions** is crucial for writing robust and maintainable Java code. Java's exception-handling mechanism categorizes exceptions based on how the compiler enforces their handling.

### **Checked Exceptions**

#### **Definition**

Checked exceptions are exceptions that the compiler **requires** you to handle explicitly. These exceptions represent scenarios where failures are anticipated, and handling them is necessary to prevent unexpected program termination.

#### **Examples**

* `IOException`: Occurs during input/output operations (e.g., file not found).
    
* `SQLException`: Indicates issues with database access.
    
* `ClassNotFoundException`: Raised when a class is not found at runtime.
    

#### **When They Occur**

Checked exceptions typically occur in operations involving external resources, such as:

* **File I/O**: Reading from or writing to files.
    
* **Database Operations**: Connecting to or querying a database.
    
* **Network Operations**: Communicating over the network.
    

#### **How to Handle Checked Exceptions**

1. **Declare with** `throws` in the Method Signature  
    If a method might throw a checked exception, you can declare it using the `throws` keyword.
    
    **Example**:
    
    ```java
    public void readFile() throws IOException {
        FileReader reader = new FileReader("file.txt");
    }
    ```
    
2. **Catch with** `try-catch` Block  
    You can handle checked exceptions directly within the method using a `try-catch` block.
    
    **Example**:
    
    ```java
    public void readFile() {
        try {
            FileReader reader = new FileReader("file.txt");
        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        }
    }
    ```
    
3. **Re-throwing Checked Exceptions**  
    If a method does not handle a checked exception, it can re-throw it to the calling method.
    
    **Example**:
    
    ```java
    public void processFile() throws IOException {
        readFile();
    }
    ```
    

#### **Key Rule**

You **must** either:

* **Catch** the checked exception with a `try-catch` block, or
    
* **Declare** it in the method signature using the `throws` keyword.
    

### **Unchecked Exceptions**

#### **Definition**

Unchecked exceptions are exceptions that the compiler **does not require** you to handle. These exceptions typically arise from programming logic errors that could be avoided with proper code checks.

#### **Examples**

* `NullPointerException`: Accessing an object reference that is `null`.
    
* `ArithmeticException`: Division by zero.
    
* `ArrayIndexOutOfBoundsException`: Accessing an invalid index in an array.
    

#### **When They Occur**

Unchecked exceptions usually stem from:

* **Programming Mistakes**: Incorrect logic or assumptions.
    
* **Invalid Operations**: Operations performed on invalid data structures (e.g., accessing beyond array bounds).
    

#### **How to Handle Unchecked Exceptions**

1. **Optional Handling with** `try-catch`  
    You can handle unchecked exceptions with a `try-catch` block, but it is not mandatory.
    
    **Example**:
    
    ```java
    public void divide(int a, int b) {
        try {
            int result = a / b;
        } catch (ArithmeticException e) {
            System.out.println("Division by zero is not allowed.");
        }
    }
    ```
    
2. **No Need to Declare**  
    You do not need to declare unchecked exceptions in the method signature with `throws`.
    
    **Example**:
    
    ```java
    public void unsafeMethod() {
        int[] nums = {1, 2, 3};
        System.out.println(nums[3]); // ArrayIndexOutOfBoundsException
    }
    ```
    

#### **Key Rule**

You are **not required** to catch or declare unchecked exceptions, but handling them can improve program robustness and user experience.

### **Summary of Checked vs. Unchecked Exceptions**

| **Aspect** | **Checked Exceptions** | **Unchecked Exceptions** |
| --- | --- | --- |
| **Compiler Enforcement** | Must be caught or declared with `throws` | Optional handling; no need to declare |
| **Typical Causes** | External issues (e.g., file, database errors) | Programming logic errors (e.g., `null` refs) |
| **Common Examples** | `IOException`, `SQLException` | `NullPointerException`, `ArithmeticException` |
| **Handling** | Mandatory `try-catch` or `throws` declaration | Optional `try-catch` |

## **Best Practices for Exception Handling in Java**

Effective exception handling is essential for writing robust, maintainable, and efficient Java programs. Here are detailed best practices to help you manage exceptions properly, ensuring that your code remains resilient and easy to debug.

### **1\. Handle Checked Exceptions Appropriately Using** `try-catch` or `throws`

Checked exceptions indicate conditions that a well-designed application should anticipate and recover from. They typically arise from operations involving external resources, such as file I/O, database access, or network communication.

#### **Best Practices for Handling Checked Exceptions**

1. **Use** `try-catch` When Recovery is Possible:  
    If you can handle the exception within the method, use a `try-catch` block to provide meaningful recovery or fallback behavior.
    
    **Example**:
    
    ```java
    public void readFile(String filePath) {
        try {
            FileReader reader = new FileReader(filePath);
            System.out.println("File read successfully.");
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filePath);
        }
    }
    ```
    
2. **Use** `throws` When You Want the Caller to Handle It:  
    If the method cannot handle the exception meaningfully, declare it with `throws` and let the caller decide how to manage it.
    
    **Example**:
    
    ```java
    public void loadFile(String filePath) throws IOException {
        FileReader reader = new FileReader(filePath);
    }
    ```
    
3. **Avoid Empty** `catch` Blocks:  
    Catching exceptions without any handling logic can mask problems and make debugging difficult.
    
    **Bad Practice**:
    
    ```java
    try {
        FileReader reader = new FileReader("data.txt");
    } catch (IOException e) {
        // Empty catch block - bad practice
    }
    ```
    

### **2\. Avoid Catching Generic Exceptions (**`Exception` or `Throwable`)

Catching broad exceptions like `Exception` or `Throwable` can obscure the specific cause of the problem and lead to unintended behavior.

#### **Why You Should Avoid This**

* **Loss of Specificity**: You might catch exceptions you didn't intend to handle.
    
* **Hides Errors**: Critical errors (e.g., `OutOfMemoryError`) may be caught unintentionally.
    
* **Harder to Debug**: You lose clarity on what went wrong.
    

**Bad Practice**:

```java
try {
    riskyOperation();
} catch (Exception e) {
    System.out.println("An error occurred."); // Too generic
}
```

#### **Better Approach**

Catch specific exceptions to handle different failure scenarios appropriately.

**Example**:

```java
try {
    riskyOperation();
} catch (FileNotFoundException e) {
    System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
    System.out.println("I/O error occurred: " + e.getMessage());
}
```

### **3\. Catch Specific Exceptions First Before Superclasses**

When catching multiple exceptions, **always catch the most specific exceptions first** before catching their superclasses. Catching a superclass exception (e.g., `Exception`) before a subclass exception (e.g., `FileNotFoundException`) will prevent the subclass handler from being executed.

#### **Example of Incorrect Order**

```java
try {
    FileReader reader = new FileReader("data.txt");
} catch (Exception e) {
    System.out.println("General exception caught.");
} catch (FileNotFoundException e) {
    // This block will never be reached
    System.out.println("File not found.");
}
```

#### **Correct Order**

```java
try {
    FileReader reader = new FileReader("data.txt");
} catch (FileNotFoundException e) {
    System.out.println("File not found.");
} catch (Exception e) {
    System.out.println("General exception caught.");
}
```

### **4\. Use Defensive Coding to Minimize Unchecked Exceptions**

Unchecked exceptions (e.g., `NullPointerException`, `ArrayIndexOutOfBoundsException`) often stem from programming logic errors. Defensive coding helps prevent these exceptions by validating inputs and assumptions before performing operations.

#### **Defensive Coding Techniques**

1. **Check for** `null` References:
    
    ```java
    public void printLength(String str) {
        if (str != null) {
            System.out.println("Length: " + str.length());
        } else {
            System.out.println("String is null.");
        }
    }
    ```
    
2. **Validate Array Indexes**:
    
    ```java
    public void accessArrayElement(int[] array, int index) {
        if (index >= 0 && index < array.length) {
            System.out.println("Element: " + array[index]);
        } else {
            System.out.println("Invalid index.");
        }
    }
    ```
    
3. **Check Divisor for Zero**:
    
    ```java
    public void divide(int a, int b) {
        if (b != 0) {
            System.out.println("Result: " + (a / b));
        } else {
            System.out.println("Cannot divide by zero.");
        }
    }
    ```
    

### **5\. Clean Up Resources Using** `finally` or Try-With-Resources

When dealing with resources such as files, sockets, or database connections, it’s important to release them properly to avoid resource leaks. Use the `finally` block or the try-with-resources statement for automatic resource management.

#### **Using** `finally` Block

The `finally` block ensures that cleanup code is executed regardless of whether an exception occurs.

**Example**:

```java
FileReader reader = null;
try {
    reader = new FileReader("data.txt");
    // Read file
} catch (IOException e) {
    System.out.println("Error reading file: " + e.getMessage());
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            System.out.println("Error closing file: " + e.getMessage());
        }
    }
}
```

#### **Using Try-With-Resources**

Introduced in Java 7, the try-with-resources statement automatically closes resources that implement the `AutoCloseable` interface.

**Example**:

```java
try (FileReader reader = new FileReader("data.txt")) {
    // Read file
    System.out.println("File read successfully.");
} catch (IOException e) {
    System.out.println("Error reading file: " + e.getMessage());
}
```

**Advantages**:

* No need for an explicit `finally` block.
    
* Simplifies code and reduces boilerplate.
    

## Code Examples for Reinforcement

### **1\. StackOverflowError**

**Explanation**: A `StackOverflowError` occurs when a method calls itself recursively without a base case or when the recursion depth exceeds the stack size. This is an `Error`, not an `Exception`, and does not need to be caught or declared. Errors typically indicate serious problems that the application cannot handle.

**Code Example**:

```java
public class StackOverflowDemo {
    public static void main(String[] args) {
        causeStackOverflow();
    }

    public static void causeStackOverflow() {
        causeStackOverflow(); // Infinite recursion
    }
}
```

**Output**:

```python
Exception in thread "main" java.lang.StackOverflowError
    at StackOverflowDemo.causeStackOverflow(StackOverflowDemo.java:6)
    at StackOverflowDemo.causeStackOverflow(StackOverflowDemo.java:6)
    ...
```

**Explanation**: The program terminates with a `StackOverflowError` and prints a stack trace. Since it is an `Error`, it is unexpected and unrecoverable.

### **2\. RuntimeExceptions**

**Explanation**: These are unchecked exceptions, meaning they do not need to be declared or caught. They indicate programming errors that can often be prevented with defensive coding. Examples include `NullPointerException`, `ArrayIndexOutOfBoundsException`, `IllegalArgumentException`, and `ArithmeticException`.

**Code Examples**:

#### **NullPointerException**

```java
public class NullPointerDemo {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length()); // Accessing a null reference
    }
}
```

**Fix**:

```java
public class NullPointerFixed {
    public static void main(String[] args) {
        String str = null;
        if (str != null) {
            System.out.println(str.length());
        } else {
            System.out.println("String is null!");
        }
    }
}
```

#### **ArrayIndexOutOfBoundsException**

```java
public class ArrayIndexDemo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        System.out.println(arr[5]); // Accessing an invalid index
    }
}
```

**Fix**:

```java
public class ArrayIndexFixed {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        int index = 5;
        if (index >= 0 && index < arr.length) {
            System.out.println(arr[index]);
        } else {
            System.out.println("Index out of bounds!");
        }
    }
}
```

#### **IllegalArgumentException**

```java
public class IllegalArgumentDemo {
    public static void setAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
        System.out.println("Age set to: " + age);
    }

    public static void main(String[] args) {
        setAge(-5); // Invalid argument
    }
}
```

**Fix**: Defensive coding already prevents the issue by throwing an exception for invalid input.

#### **ArithmeticException**

```java
public class ArithmeticDemo {
    public static void main(String[] args) {
        int result = 10 / 0; // Division by zero
        System.out.println("Result: " + result);
    }
}
```

**Fix**:

```java
public class ArithmeticFixed {
    public static void main(String[] args) {
        int divisor = 0;
        if (divisor != 0) {
            System.out.println("Result: " + (10 / divisor));
        } else {
            System.out.println("Cannot divide by zero!");
        }
    }
}
```

### **3\. Custom Checked Exception**

**Explanation**: Create a custom checked exception by extending `Exception`.

**Code Example**:

```java
public class CustomCheckedException extends Exception {
    public CustomCheckedException(String message) {
        super(message);
    }
}
```

### **4\. Method Throwing Multiple Checked Exceptions**

**Code Example**:

```java
import java.io.IOException;

public class MultipleExceptionsDemo {
    public void riskyMethod() throws IOException, CustomCheckedException {
        if (Math.random() > 0.5) {
            throw new IOException("IO problem occurred");
        } else {
            throw new CustomCheckedException("Custom problem occurred");
        }
    }
}
```

### **5\. Handling Multiple Checked Exceptions**

**Code Example**:

#### **Catch one, declare one**:

```java
public void handleOne() throws IOException {
    try {
        new MultipleExceptionsDemo().riskyMethod();
    } catch (CustomCheckedException e) {
        System.out.println("Handled CustomCheckedException: " + e.getMessage());
    }
}
```

#### **Catch both, no declaration**:

```java
public void handleBoth() {
    try {
        new MultipleExceptionsDemo().riskyMethod();
    } catch (IOException | CustomCheckedException e) {
        System.out.println("Handled exception: " + e.getMessage());
    }
}
```

#### **Catch both, rethrow one**:

```java
public void handleAndRethrow() throws IOException {
    try {
        new MultipleExceptionsDemo().riskyMethod();
    } catch (CustomCheckedException e) {
        System.out.println("Handled CustomCheckedException: " + e.getMessage());
    } catch (IOException e) {
        System.out.println("Caught IOException: " + e.getMessage());
        throw e;
    }
}
```

#### **Catch both, rethrow new exception**:

```java
public void handleAndThrowNew() throws Exception {
    try {
        new MultipleExceptionsDemo().riskyMethod();
    } catch (IOException | CustomCheckedException e) {
        throw new Exception("Wrapped Exception: " + e.getMessage());
    }
}
```

### **6\. Nested Try-Catch**

**Code Example**:

```java
public class NestedTryCatch {
    public static void main(String[] args) {
        try {
            try {
                throw new IOException("Inner exception");
            } catch (IOException e) {
                System.out.println("Inner catch: " + e.getMessage());
                throw e; // Rethrow
            }
        } catch (IOException e) {
            System.out.println("Outer catch: " + e.getMessage());
        }
    }
}
```

### **7\. Custom Exceptions with Inheritance**

**Code Example**:

```java
public class ParentException extends Exception {}
public class ChildException extends ParentException {}

public class ExceptionOrderDemo {
    public static void main(String[] args) {
        try {
            throw new ChildException();
        } catch (ParentException e) {
            System.out.println("Caught ParentException");
        } catch (ChildException e) { // Unreachable
            System.out.println("Caught ChildException");
        }
    }
}
```

**Explanation**: Catching `ParentException` first makes the `ChildException` catch block unreachable, resulting in a compilation error. Always catch specific exceptions before general ones.

### **8\. Code in** `finally` is Always Executed

**Explanation**: The `finally` block is executed no matter what happens in the `try` block—whether an exception is thrown, caught, or no exception occurs. This is typically used for cleanup operations like closing resources.

**Code Example**:

```java
public class FinallyAlwaysExecutes {
    public static void main(String[] args) {
        try {
            System.out.println("In try block");
            int result = 10 / 0; // This will throw ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }
}
```

**Output**:

```python
In try block
Exception caught: / by zero
Finally block executed
```

**Explanation**: Even though an exception is thrown and caught, the `finally` block is still executed.

### **9.** `finally` Executes Even Without a `catch`

**Explanation**: The `finally` block will always execute even if there is no `catch` block. If an exception is thrown but not caught, the `finally` block is executed before the exception propagates further.

**Code Example**:

```java
public class FinallyWithoutCatch {
    public static void main(String[] args) {
        try {
            System.out.println("In try block");
            int result = 10 / 0; // This will throw ArithmeticException
        } finally {
            System.out.println("Finally block executed");
        }
        // No catch block here
    }
}
```

**Output**:

```python
In try block
Finally block executed
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at FinallyWithoutCatch.main(FinallyWithoutCatch.java:5)
```

**Explanation**: The `finally` block is executed, and then the uncaught exception terminates the program.
