🌟 Chapter 35: Exception Handling (Part 2) - Master Exception Handling in Java! 🌟
Table of contents
- 1. Introduction
- 2. Previous Class Recap: Basics of Exception Handling
- 3. Case Studies on Exception Handling
- 4. Understanding Runtime Stack Mechanism
- Code Example:
- Example Output (if num2 = 0):
- Runtime Stack Analysis:
- 5. Exception Handling Techniques
- Keywords in Exception Handling:
- 5.1 Handling Exception(try-catch) -
- Table: Process of Exception Propagation
- Code Example:
- Explanation of Code and Process:
- Output (when num2 = 0):
- Key Observations:
- 5.2 Ducking Exceptions Using throws
- 5.2.1 Concept of Ducking Exceptions with Examples
- 5.2.2 Code Example with Method Signature
- 5.2.3 Importance of Declaring Exceptions in Method Signature
- Code Flow Explanation:
- Sample Output (when num2 is 0):
- Conclusion:
- 5.3 Re-Throwing Exceptions
- 6. Keywords in Exception Handling
- 7. Real-World Examples
- 8. Best Practices in Exception Handling
- 9. Advanced Concepts in Exception Handling
- 10. When to Duck an Exception?
- 11. Example of Checked Exception (Thread Sleep)
- 12. Exception Propagation and Stack Hierarchy
- 13. Re-Throwing an Exception
- 14. Important Points
- 15. Throw vs. Throws
- 16. Components of an Exception Object
- 17. Three Methods Inside Catch Block We Usually Write
- 18. Finally Block Execution
- Conclusion:
- Related Posts in My Series:
- Other Series:
1. Introduction
1.1 Importance of Exception Handling in Software Development
Exception handling is a fundamental aspect of building reliable and robust software. Its importance lies in:
Maintaining Application Stability: Without exception handling, a program would abruptly terminate whenever an error occurs, leading to a poor user experience.
Debugging and Error Logging: Exception handling provides mechanisms like stack traces, which help identify and debug issues effectively.
Resource Management: Proper exception handling ensures critical resources like files, database connections, and memory are cleaned up even in the face of errors.
Encapsulation of Error Logic: It keeps the core business logic clean by segregating error-handling code.
Enhanced User Experience: Graceful error messages can guide users rather than presenting them with cryptic system-generated errors.
1.2 Key Terms
Exception: An event during program execution that disrupts the normal flow of instructions, such as
ArithmeticException
,IOException
, etc.Try-Catch Block: A construct to handle exceptions. Code that may throw an exception is written inside a
try
block, while thecatch
block provides the handling logic.try { int result = 10 / 0; // This will throw ArithmeticException } catch (ArithmeticException e) { System.out.println("Division by zero is not allowed."); }
Throws Keyword: Used in method declarations to indicate the method might throw specific exceptions, which need to be handled by the calling code.
void readFile() throws IOException { // File reading logic }
Finally Block: A block of code that always executes after the
try
andcatch
blocks, regardless of whether an exception occurred. It’s often used for resource cleanup.
1.3 Overview of Java's Exception Hierarchy
Java's exception hierarchy is rooted in the Throwable
class and divides into two main branches:
Checked Exceptions:
Must be declared in the
throws
clause or handled usingtry-catch
.Represents recoverable conditions (e.g.,
IOException
,SQLException
).
Unchecked Exceptions:
Do not need explicit handling.
Represent programming errors (e.g.,
NullPointerException
,ArrayIndexOutOfBoundsException
).
Hierarchy Diagram:
Throwable
├── Error (e.g., OutOfMemoryError)
└── Exception
├── Checked Exceptions (e.g., IOException)
└── RuntimeException (Unchecked Exceptions, e.g., NullPointerException)
2. Previous Class Recap: Basics of Exception Handling
2.1 What is an Exception, and Why is it Crucial to Handle?
An exception is an unexpected event that occurs during program execution, disrupting its normal flow. Examples include dividing by zero, accessing an invalid array index, or attempting to open a non-existent file.
Handling exceptions is crucial because:
It prevents abrupt program termination.
It allows the program to recover and continue running.
It ensures that resources like files and database connections are properly closed.
2.2 Basic Try-Catch Block Syntax and Flow
The try-catch
block is the cornerstone of exception handling in Java. Its syntax is as follows:
try {
// Code that might throw an exception
} catch (ExceptionType e) {
// Code to handle the exception
}
Flow:
The
try
block is executed.If an exception occurs:
The control shifts to the corresponding
catch
block.Exception details are captured in the
catch
parameter (e
in this case).
If no exception occurs, the
catch
block is skipped.
Example:
try {
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // Throws ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid index accessed!");
}
2.3 Brief Overview of Keywords Associated with Exception Handling
try: Defines the block of code that might throw an exception.
catch: Catches and handles exceptions thrown in the
try
block.finally: A block that always executes, commonly used for cleanup tasks.
throws: Used in method signatures to indicate the exceptions a method might throw.
throw: Used to explicitly throw an exception from code.
throw new IllegalArgumentException("Invalid argument");
These concepts lay the groundwork for mastering exception handling in Java, ensuring robust and maintainable code.
3. Case Studies on Exception Handling
3.1 Case 1: No Exception Raised
3.1.1 Statements Executed When No Exception Occurs When no exception occurs inside the try
block, all the statements within the block will be executed sequentially, and the catch
block is skipped. The program continues executing normally after the try-catch
structure.
3.1.2 Code Example
statement-1;
try {
statement-2; // Executed
statement-3; // Executed
statement-4; // Executed
} catch (Exception e) {
statement-5; // Skipped (no exception)
}
statement-6; // Executed
3.1.3 Execution Flow Explained
statement-1: Executed first, outside of the
try-catch
block.statement-2: Executed as no exception occurs yet.
statement-3: Executed next.
statement-4: Finally, this statement is executed within the
try
block.catch block: The
catch
block is skipped since no exception was raised.statement-6: Executed after the
try-catch
block.
In this case, everything executes in sequence, and the program finishes normally without interruption.
3.2 Case 2: Exception Raised and Catch Block Matched
3.2.1 Execution When an Exception is Raised at Statement-3 If an exception occurs in the try
block (e.g., at statement-3), and there is a corresponding catch
block to handle that exception, the following happens:
Statements before the exception are executed.
The program control moves to the
catch
block when an exception is thrown.The
catch
block handles the exception, allowing the program to continue execution normally after the block.
3.2.2 Code Example and Explanation
statement-1;
try {
statement-2; // Executed
statement-3; // Exception raised here (e.g., ArithmeticException)
statement-4; // Skipped (because exception occurred)
} catch (ArithmeticException e) {
statement-5; // Executed (exception caught here)
}
statement-6; // Executed
3.2.3 Output Analysis
statement-1: Executed first.
statement-2: Executed without error.
statement-3: Exception occurs (e.g., division by zero).
catch block: Catches the exception and executes statement-5.
statement-6: Executed after the
catch
block, leading to normal termination.
The program does not terminate abruptly because the exception was caught and handled, allowing for a smooth continuation after the exception.
3.3 Case 3: Exception Raised and No Matching Catch Block
3.3.1 Consequences of Unmatched Exception Handling When an exception occurs, but there is no corresponding catch
block to handle the specific exception type, the program will terminate abnormally. The exception is propagated up the call stack until it is either caught by a matching catch
block or the default exception handler.
3.3.2 Code Snippet and Analysis
statement-1;
try {
statement-2; // Executed
statement-3; // Exception occurs here
statement-4; // Skipped
} catch (NullPointerException e) {
statement-5; // This block doesn't catch the ArithmeticException
}
statement-6; // Not reached due to abnormal termination
3.3.3 Output Analysis
statement-1: Executed first.
statement-2: Executed without error.
statement-3: Throws an
ArithmeticException
(butcatch
block only handlesNullPointerException
).The exception is not caught by the
catch
block, leading to abnormal termination of the program.statement-6: Not reached due to the exception propagation and program termination.
In this case, the program terminates unexpectedly because there is no matching catch
block for the type of exception thrown.
3.4 Case 4: Exception Raised Outside the Try Block
3.4.1 Explaining Risky Code Outside the Try Block If risky code that can throw exceptions is not placed inside the try
block, the exception will not be caught by the try-catch
structure. Instead, it will be handled by the default exception handler, and the program may terminate.
3.4.2 Default Handler’s Role and Program Termination If the exception occurs outside the try
block (e.g., in statement-1, statement-5, or statement-6), there is no try-catch
structure to catch it, and the default Java handler will deal with it. This often results in an abnormal termination of the program.
3.4.3 Visualizing the Stack Using Diagrams Consider the following example where an exception is raised outside the try
block:
statement-1; // Exception occurs here
try {
statement-2;
statement-3;
} catch (Exception e) {
statement-4;
}
statement-5; // Exception will prevent reaching this statement
statement-1: If an exception occurs here, no
try-catch
block is present, and it causes an immediate program halt.The stack trace is printed, showing where the exception occurred.
The program does not proceed to statement-5, and the default exception handler will terminate the program.
Conclusion This case demonstrates the importance of placing all risky code inside the try
block to ensure that exceptions are properly handled, preventing unexpected program crashes.
4. Understanding Runtime Stack Mechanism
4.1 How Control Flows During Exception Handling
When an exception occurs in a Java program, the control flow shifts through the stack as the exception propagates. This process involves the JVM examining each stack frame, starting from the method where the exception was raised, to find an appropriate catch
block to handle it. If no handler is found, the JVM eventually invokes the default exception handler, leading to abnormal termination of the program.
4.2 Steps in the Runtime Stack Process
4.2.1 Main Method Record Creation
When a Java program starts execution, the main method is invoked. The JVM creates a "stack frame" for the main method and pushes it onto the runtime stack.
This stack frame holds information such as local variables, method parameters, and the current state of execution for the main method.
4.2.2 Alpha Method Invocation
The main method calls the
alpha()
method. A new stack frame foralpha()
is created and pushed onto the runtime stack, sitting on top of the main method's stack frame.The execution of the code inside the
alpha()
method begins.
4.2.3 Exception Object Creation in alpha()
During the execution of the
alpha()
method, if an exception occurs (for example, a division by zero), the JVM creates an exception object (in this case,ArithmeticException
).This exception object is associated with the specific error that occurred, such as an attempt to divide by zero.
4.2.4 Propagation Through the Stack Hierarchy
The exception object is passed to the JVM, which first checks if the exception is handled within the
alpha()
method.If
alpha()
does not have acatch
block to handle this exception, the JVM automatically propagates the exception to the calling method, which in this case is themain()
method.The
main()
method’s stack frame is checked to see if it has a handler for the exception. If not, the JVM proceeds to the default exception handler.
4.3 Default Handler’s Role and Its Impact
If the exception is not caught within the
alpha()
method or themain()
method, the JVM invokes the default exception handler.The default handler terminates the program and prints a stack trace, which includes information about where the exception occurred (e.g., the line number in the code) and the type of exception (e.g.,
ArithmeticException: / by zero
).
Code Example:
import java.util.Scanner;
class Alpha {
void alpha() {
System.out.println("Connection to calc is established");
// Taking input from console
Scanner scan = new Scanner(System.in);
System.out.println("Enter num1 to divide:");
int num1 = scan.nextInt();
System.out.println("Enter num2 to divide:");
int num2 = scan.nextInt();
// Division operation that may cause an exception
int res = num1 / num2; // Division by zero exception if num2 = 0
System.out.println("The result: " + res);
System.out.println("Connection is terminated");
}
}
public class Exception9 {
public static void main(String[] args) {
Alpha a = new Alpha();
a.alpha(); // Calling alpha method
}
}
Example Output (if num2 = 0
):
Connection to calc is established
Enter num1 to divide:
100
Enter num2 to divide:
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Alpha.alpha(Exception9.java:46)
at Exception9.main(Exception9.java:57)
Runtime Stack Analysis:
Main Method Execution: The program starts execution from the
main
method.Alpha Method Call: The
main
method callsa.alpha()
, creating a new stack frame for thealpha()
method and placing it on top of the main method's stack frame.Division by Zero: Inside
alpha()
, when the division by zero occurs, the JVM creates anArithmeticException
object and searches for a matchingcatch
block.No Handler in Alpha: Since there is no
catch
block inalpha()
, the exception is propagated to themain()
method.No Handler in Main: The
main()
method also does not have a handler for this exception, so the JVM proceeds to the default exception handler.Program Termination: The default exception handler terminates the program and prints the stack trace, indicating where the exception occurred.
In this case, the exception leads to abnormal termination because no catch
block was present to handle it.
5. Exception Handling Techniques
Whenever there is an exception, we can choose to handle it in three main ways:
Handle the exception using
try-catch
blocks.Duck the exception using the
throws
keyword.Re-throw the exception using
throw
,throws
,try
,catch
, andfinally
.
Keywords in Exception Handling:
The following are the key keywords used in Java's exception handling mechanism:
try
: Defines a block of code to monitor for exceptions.catch
: Catches exceptions thrown from thetry
block.throw
: Used to explicitly throw an exception.throws
: Declares that a method may throw an exception.finally
: Defines a block of code that will always execute after thetry-catch
block, regardless of whether an exception was thrown or not.
5.1 Handling Exception(try-catch) -
Step-by-step process:
Control Flow:
The control starts in the
main
method.From
main
, thebeta1()
method is called.In
beta1()
, thealpha1()
method is invoked.In the
alpha1()
method, an exception may occur (e.g., division by zero).
Exception Handling:
If an exception occurs, the JVM creates an exception object and looks for an appropriate handler.
If no handler is found in
alpha1()
, the exception is propagated tobeta1()
.If no handler is found in
beta1()
, the exception is propagated to themain()
method.The exception is caught in
main()
, and the program continues to run.
Table: Process of Exception Propagation
Step | Method | Action |
1. Method Call | main() | The program starts execution in the main() method. |
2. Method Invocation | beta1() | The main() method calls beta1() . |
3. Method Invocation | alpha1() | beta1() calls the alpha1() method. |
4. Exception Occurs | alpha1() | An exception occurs (e.g., division by zero), and an exception object is created. |
5. Propagation to Caller | beta1() | The exception propagates to beta1() as it has no handler for the exception. |
6. Propagation to Caller | main() | If no handler in beta1() , the exception propagates to main() . |
7. Exception Handled | main() | The main() method catches the exception and handles it using catch . |
8. Program Terminates | main() | After handling the exception, the program terminates. |
Code Example:
import java.util.Scanner;
class Alpha1 {
void alpha1() {
System.out.println("Connection to calc is established");
// Taking input from console
Scanner scan = new Scanner(System.in);
System.out.println("Enter num1 to divide:");
int num1 = scan.nextInt();
System.out.println("Enter num2 to divide:");
int num2 = scan.nextInt();
// Division operation that may cause an exception
int res = num1 / num2; // Division by zero exception if num2 = 0
System.out.println("The result: " + res);
System.out.println("Connection is terminated");
}
}
class Beta1 {
void beta1() {
Alpha1 a = new Alpha1();
a.alpha1();
}
}
public class Exception10 {
public static void main(String[] args) {
try {
Beta1 b = new Beta1();
b.beta1();
} catch (ArithmeticException ae) {
System.out.println("Exception finally handled in main");
}
}
}
Explanation of Code and Process:
Main Method (
main()
):- The
main()
method begins execution and calls thebeta1()
method.
- The
Beta Method (
beta1()
):- The
beta1()
method calls thealpha1()
method, which contains the logic for dividing two numbers.
- The
Alpha Method (
alpha1()
):The user is prompted for two numbers. If the second number (
num2
) is 0, division by zero occurs.The JVM throws an
ArithmeticException
when this happens.
Exception Handling:
In
alpha1()
: The exception is not handled, so it propagates up the call stack.In
beta1()
: The exception is still not handled, so it propagates further up.In
main()
: Themain()
method has acatch
block that handles theArithmeticException
and prints the message: "Exception finally handled in main."
Output (when num2 = 0
):
Connection to calc is established
Enter num1 to divide:
100
Enter num2 to divide:
0
Exception finally handled in main
Key Observations:
Automatic Exception Propagation:
- If an exception is not handled in a method, it is automatically propagated up the call stack (from
alpha1()
→beta1()
→main()
).
- If an exception is not handled in a method, it is automatically propagated up the call stack (from
Exception Handling in the
main()
Method:- The
main()
method successfully handles the exception, preventing the program from terminating abruptly and ensuring a graceful end.
- The
5.2 Ducking Exceptions Using throws
Ducking Exceptions refers to the process of not handling an exception within the method where it occurs, but instead passing the responsibility of handling the exception to the calling method. This is done by using the throws
keyword in the method signature. It is a way to inform the caller of the method that there is a possibility of an exception occurring, and it needs to handle it.
5.2.1 Concept of Ducking Exceptions with Examples
Ducking an exception means we avoid handling an exception inside a method and delegate the responsibility to the method that calls it. It is also known as propagating the exception.
This is particularly useful when we can't handle the exception at the current level but want to ensure that the exception is eventually caught and handled by the caller.
In Java, we achieve this by adding the throws
keyword to the method signature, followed by the exception type(s) that the method might throw.
5.2.2 Code Example with Method Signature
Let’s understand the flow of exception handling using throws
with the following code:
import java.util.Scanner;
class Alpha2 {
// This method declares that it might throw an ArithmeticException
void alpha2() throws ArithmeticException {
// Connection to calculator application
System.out.println("Connection to calc is established");
// Taking input from the console
Scanner scan = new Scanner(System.in);
System.out.println("Enter num1 to divide:");
int num1 = scan.nextInt();
System.out.println("Enter num2 to divide:");
int num2 = scan.nextInt();
// Risky operation: Division that might throw ArithmeticException (if num2 == 0)
int res = num1 / num2;
System.out.println("The result: " + res);
// Terminating the connection
System.out.println("Connection is terminated");
}
}
class Beta2 {
// This method declares that it might throw an ArithmeticException
void beta2() throws ArithmeticException {
Alpha2 a = new Alpha2();
a.alpha2(); // Call to alpha2, which may throw an exception
}
}
public class Exception11 {
public static void main(String[] args) {
try {
Beta2 b = new Beta2();
b.beta2(); // Call to beta2, which may propagate the exception
} catch (ArithmeticException ae) {
// Handling the exception in the main method
System.out.println("Exception finally handled in main");
}
}
}
5.2.3 Importance of Declaring Exceptions in Method Signature
Passing Responsibility to the Caller:
When you declare an exception using
throws
, you are passing the responsibility of handling that exception to the caller. This is useful when you don't want or cannot handle the exception in the current method but want to make the caller aware of the possibility of it occurring.For example, in the method
alpha2()
, the exception is not handled using atry-catch
block. Instead, the method signature includesthrows ArithmeticException
, notifying the caller (in this case,beta2()
) that anArithmeticException
may be thrown.
Caution to the Caller:
By declaring exceptions in the method signature, the caller is forced to acknowledge the possibility of the exception and handle it appropriately.
In the example,
beta2()
callsalpha2()
, and sincealpha2()
can throw anArithmeticException
,beta2()
also declaresthrows ArithmeticException
in its signature. Thus, the caller ofbeta2()
(in themain
method) will be aware of the potential exception and can handle it with atry-catch
block.
Avoiding Overly Complex Methods:
- By using
throws
, the method doesn’t get cluttered with multipletry-catch
blocks for every possible exception. Instead, it delegates the exception handling responsibility to higher-level methods that can manage these exceptions properly.
- By using
Code Clarity and Maintainability:
- Declaring exceptions explicitly in the method signature helps other developers or future maintainers of the code understand which exceptions might occur when calling a method. This makes the code more readable and maintainable.
Code Flow Explanation:
Control Flow in Main:
First, control enters the
main
method.The
main
method callsbeta2()
, which is responsible for invokingalpha2()
.
Call to
alpha2()
:The method
alpha2()
executes and performs the division operation, which can throw anArithmeticException
ifnum2
is zero.Instead of handling this exception within
alpha2()
, the method declaresthrows ArithmeticException
in its signature, effectively passing the responsibility for handling the exception to the calling method (which isbeta2()
).
Call to
beta2()
:- The method
beta2()
callsalpha2()
, but sincealpha2()
declares that it might throw anArithmeticException
,beta2()
also declaresthrows ArithmeticException
in its method signature. This ensures that any calling method (in this case,main()
) is made aware of the potential exception.
- The method
Handling Exception in Main:
When control returns to the
main
method, it catches theArithmeticException
thrown byalpha2()
(throughbeta2()
), and prints the message"Exception finally handled in main"
.This is where the exception is actually handled, ensuring the program does not terminate unexpectedly.
Sample Output (when num2
is 0):
Connection to calc is established
Enter num1 to divide:
100
Enter num2 to divide:
0
Exception finally handled in main
Conclusion:
Ducking an exception using the
throws
keyword allows a method to pass the responsibility of handling an exception to the calling method, without taking the burden of handling it internally.By declaring exceptions in the method signature, you are improving the clarity, maintainability, and responsibility management in your code.
Important Takeaways:
Always declare exceptions in the method signature using
throws
if the method can throw an exception, especially when you're not handling the exception inside the method.The caller is then responsible for handling the exception, either by catching it or propagating it further up the call stack.
Ducking exceptions is particularly useful in scenarios where you want to allow the caller to decide how to handle specific exceptions, such as in the case of checked exceptions or runtime exceptions.
5.3 Re-Throwing Exceptions
5.3.1 Use of throw
and throws
for re-throwing exceptions.
5.3.2 Practical scenarios for re-throwing exceptions.
6. Keywords in Exception Handling
6.1 Overview of Java exception-handling keywords.
6.2 Purpose and usage of:
6.2.1
try
6.2.2
catch
6.2.3
throw
6.2.4
throws
6.2.5
finally
7. Real-World Examples
7.1 Simulating ArithmeticException in division:
int result = num1 / num2;
7.2 Stack mechanism during exception propagation.
7.3 Handling user input errors gracefully.
8. Best Practices in Exception Handling
8.1 Writing clean and maintainable exception-handling code.
8.2 Avoiding common pitfalls like empty catch blocks.
8.3 Recommendations for unchecked exceptions:
Always handle critical exceptions.
Avoid unnecessary ducking of unchecked exceptions.
9. Advanced Concepts in Exception Handling
9.1 Custom exceptions: Defining and using them.
9.2 Exception chaining and its benefits.
9.3 Use of logging frameworks for better debugging.
10. When to Duck an Exception?
Usually, developers duck checked exceptions.
There are two types of exceptions in Java:
Unchecked Exceptions:
The compiler will not force you to handle them.
They are not checked during compile time, only at runtime.
Developers identify risky code during development.
Checked Exceptions:
These exceptions must be handled, or the compiler will not allow the code to run.
The risky code is checked during compilation.
The compiler ensures there’s no chance of abrupt termination.
11. Example of Checked Exception (Thread Sleep)
Example 1: Surround with Try-Catch
public class Exception12 {
public static void main(String[] args) {
try {
Thread.sleep(4000); // risky code
} catch (InterruptedException e) {
e.printStackTrace(); // handles the exception
}
}
}
Example 2: Ducking an Exception
public class Exception12 {
public static void main(String[] args) throws InterruptedException {
System.out.println("Before sleep");
Thread.sleep(4000); // risky code
System.out.println("After sleep"); // output displayed after 4 seconds
}
}
12. Exception Propagation and Stack Hierarchy
If an exception occurs and is not handled in the current stack frame, it is propagated down the call stack to the caller.
Example: Alpha3 and Main
Handler in Alpha3:
- Exception handled locally; it does not propagate further.
Handler in Main:
- If not handled in Alpha3, the exception propagates to Main.
Example 1: Try-Catch in Alpha3
class Alpha3 {
void alpha3() {
System.out.println("Connection to Calc app established");
try {
int res = 10 / 0; // risky code
System.out.println("The result is " + res);
} catch (ArithmeticException e) {
System.out.println("Exception handled in Alpha3");
}
}
}
public class Exception13 {
public static void main(String[] args) {
Alpha3 a = new Alpha3();
a.alpha3();
}
}
13. Re-Throwing an Exception
Re-throwing an exception propagates a handled exception to the caller.
Use the
throw
keyword inside a catch block.
Example: Re-Throwing with Finally Block
class Alpha4 {
void alpha4() throws ArithmeticException {
try {
int res = 10 / 0; // risky code
System.out.println("The result is " + res);
} catch (ArithmeticException e) {
System.out.println("Exception handled in Alpha4");
throw e; // re-throws the exception
} finally {
System.out.println("Connection terminated");
}
}
}
public class Exception14 {
public static void main(String[] args) {
try {
Alpha4 a = new Alpha4();
a.alpha4();
} catch (ArithmeticException e) {
System.out.println("Exception handled in main");
}
}
}
14. Important Points
The
throw
keyword is used to re-throw exceptions.It is written in the catch block.
Statements after the
throw
keyword are not executed.Use the
finally
block for critical cleanup operations, ensuring it always executes.
15. Throw vs. Throws
Keyword | Purpose | Usage | Behavior |
throw | Re-throws an exception to the caller | Inside methods or catch blocks | Skips code below and propagates the error |
throws | Indicates an exception may be thrown | Method signature | Alerts the caller to handle exceptions |
16. Components of an Exception Object
Exception Name:
- Example:
ArithmeticException
- Example:
Description:
- Example: "Division by zero"
Stack Trace:
- Identifies the stack frame and line number where the exception occurred.
17. Three Methods Inside Catch Block We Usually Write
In Java, there are three commonly used methods inside a catch
block to print information about exceptions. These methods are part of the Throwable
class, which is the superclass of all errors and exceptions in Java.
Methods to Print Exception Information:
getMessage()
: Prints the description of the exception.- Example output:
/ by zero
- Example output:
toString()
: Prints the name of the exception class and its description.Example output:
java.lang.ArithmeticException: / by zero
printStackTrace()
: Prints the name of the exception, its description, and the complete stack trace showing where the exception occurred.Example output:
java.lang.ArithmeticException: / by zero at Alpha6.alpha6(Exception16.java:28) at Exception16.main(Exception16.java:45)
Example Program:
import java.util.Scanner;
class Alpha6 {
void alpha6() {
System.out.println("Connection to Calc app is established");
try {
Scanner scan = new Scanner(System.in);
System.out.println("Enter the first number to divide:");
int num1 = scan.nextInt();
System.out.println("Enter the second number to divide:");
int num2 = scan.nextInt();
int res = num1 / num2; // Division operation
System.out.println("The result is: " + res);
} catch (ArithmeticException e) {
System.out.println("Please provide valid input.");
System.out.println("Description: " + e.getMessage()); // Method 1
System.out.println("Details: " + e.toString()); // Method 2
e.printStackTrace(); // Method 3
}
}
}
public class Exception16 {
public static void main(String[] args) {
Alpha6 alpha = new Alpha6();
alpha.alpha6();
}
}
Output (Example for Division by Zero):
Connection to Calc app is established
Enter the first number to divide:
100
Enter the second number to divide:
0
Please provide valid input.
Description: / by zero
Details: java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Alpha6.alpha6(Exception16.java:28)
at Exception16.main(Exception16.java:45)
18. Finally Block Execution
The finally
block in Java is used to ensure that certain critical code is executed, regardless of whether an exception is thrown or caught. It is commonly used to release resources like closing files or database connections.
Purpose of the finally
Block:
Resource Management: Ensures resources like files, database connections, or network sockets are closed.
Guaranteed Execution: Runs whether an exception is handled or not, except in specific cases like
System.exit()
.
Behavior of the finally
Block:
Exception Raised | Exception Handled | Finally Executed |
No | - | Yes |
Yes | Yes | Yes |
Yes | No | Yes |
Examples of Scenarios:
No Exception Occurs:
try { System.out.println("Inside try"); } catch (Exception e) { System.out.println("Inside catch"); } finally { System.out.println("Inside finally"); }
Output:
Inside try Inside finally
Exception Occurs and is Caught:
try { System.out.println("Inside try"); System.out.println(10 / 0); // ArithmeticException } catch (ArithmeticException e) { System.out.println("Inside catch"); } finally { System.out.println("Inside finally"); }
Output:
Inside try Inside catch Inside finally
Exception Occurs and is Not Caught:
try { System.out.println("Inside try"); System.out.println(10 / 0); // ArithmeticException } catch (NullPointerException e) { System.out.println("Inside catch"); } finally { System.out.println("Inside finally"); }
Output:
Inside try Inside finally Exception in thread "main" java.lang.ArithmeticException: / by zero
Nested try-catch
and finally
:
You can write a try-catch
block inside a finally
block to handle any exceptions that occur during resource cleanup.
Conclusion:
Understanding Exception Handling in Java is critical for writing robust and fault-tolerant applications. The knowledge gained from the discussed points forms a comprehensive foundation for exception handling. Here's the summary of the concepts:
Definition and Need for Exception Handling: Exceptions are runtime anomalies that can disrupt normal program flow. Exception handling mechanisms (
try-catch-finally
) ensure graceful degradation of the program instead of abrupt termination.Hierarchy of Exceptions: The Java Exception Hierarchy illustrates that all exception classes inherit from the
Throwable
class. It further branches into:Checked Exceptions: Must be declared or handled explicitly (e.g.,
IOException
).Unchecked Exceptions: Occur at runtime and don't require mandatory handling (e.g.,
NullPointerException
).
Three Exception Information Methods:
getMessage()
: Provides a description of the exception.toString()
: Displays the exception class name and description.printStackTrace()
: Prints the exception's stack trace for debugging.
finally
Block Usage: Ensures resource cleanup, such as closing files or database connections, and always executes regardless of exception occurrence or handling.Exception Scenarios: Various cases like matching/non-matching catch blocks, nested try-catch blocks, and exceptions within finally blocks demonstrate Java's exception control flow.
Unchecked vs. Checked Exceptions: Java classifies exceptions to emphasize compile-time checking for critical issues, while runtime exceptions may be ignored if appropriate.
Partially Checked Exceptions: The
Exception
class acts as a parent to both checked and unchecked exceptions, making it partially checked. Subclasses likeInterruptedException
(checked) andNullPointerException
(unchecked) illustrate this concept.Custom/User-Defined Exceptions: Java allows creating custom exception classes to handle specific scenarios not addressed by built-in exceptions.
Nested Exception Handling: Writing nested
try-catch
blocks provides granularity in managing exceptions at different code levels, ensuring specific errors are handled where they occur.Practical Examples: Real-world implementations like division operations and file-handling code snippets demonstrate how to implement exception handling effectively.
Abnormal vs. Normal Termination: Depending on the occurrence and handling of exceptions, programs can terminate gracefully (normal) or abruptly (abnormal). Proper handling avoids crashes.
finally
in Edge Cases: Thefinally
block gets executed even in scenarios like non-matching catch blocks or exceptions raised within itself. This makes it indispensable for guaranteed cleanup.Exception Classes in
java.lang
Package: All standard exception classes belong to thejava.lang
package, eliminating the need for explicit import.Illustrations and Diagrams: Exception hierarchies and control flow diagrams aid in understanding relationships between exception types and program flow.
Best Practices in Exception Handling:
Use specific exception classes over general ones (
Exception
orThrowable
).Always close resources using
finally
or try-with-resources.Avoid using exceptions for control flow; handle only exceptional scenarios.
By grasping these concepts, Java developers can effectively manage errors and build reliable, maintainable applications. This comprehensive guide sets the stage for exploring more advanced topics like multi-threaded exception handling and custom error frameworks.
Related Posts in My Series:
Chapter 3:Mastering Git: Installation, Architecture, and Collaborative Workflow for Developers
Chapter 34:Method Hiding Conclusion and Introduction to Exception Handling
Other Series:
Connect with Me
Stay updated with my latest posts and projects by following me on social media:
LinkedIn: Connect with me for professional updates and insights.
GitHub: Explore my repository and contributions to various projects.
LeetCode: Check out my coding practice and challenges.
Your feedback and engagement are invaluable. Feel free to reach out with questions, comments, or suggestions. Happy coding!
Rohit Gawande
Full Stack Java Developer | Blogger | Coding Enthusiast
Subscribe to my newsletter
Read articles from Rohit Gawande directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Rohit Gawande
Rohit Gawande
🚀 Tech Enthusiast | Full Stack Developer | System Design Explorer 💻 Passionate About Building Scalable Solutions and Sharing Knowledge Hi, I’m Rohit Gawande! 👋I am a Full Stack Java Developer with a deep interest in System Design, Data Structures & Algorithms, and building modern web applications. My goal is to empower developers with practical knowledge, best practices, and insights from real-world experiences. What I’m Currently Doing 🔹 Writing an in-depth System Design Series to help developers master complex design concepts.🔹 Sharing insights and projects from my journey in Full Stack Java Development, DSA in Java (Alpha Plus Course), and Full Stack Web Development.🔹 Exploring advanced Java concepts and modern web technologies. What You Can Expect Here ✨ Detailed technical blogs with examples, diagrams, and real-world use cases.✨ Practical guides on Java, System Design, and Full Stack Development.✨ Community-driven discussions to learn and grow together. Let’s Connect! 🌐 GitHub – Explore my projects and contributions.💼 LinkedIn – Connect for opportunities and collaborations.🏆 LeetCode – Check out my problem-solving journey. 💡 "Learning is a journey, not a destination. Let’s grow together!" Feel free to customize or add more based on your preferences! 😊