Java Programming
Lecture 13: Exception Handling Fundamentals
Course: 4343203 - Java Programming
GTU Semester 4 | Unit 3
Learning Objectives:
- Understand exception concepts and hierarchy
- Master try-catch-finally blocks
- Learn different types of exceptions
- Apply proper exception handling techniques
- Create robust and fault-tolerant programs
Understanding Exceptions
Exception is an abnormal condition or event that disrupts the normal flow of program execution. It's a runtime error that can be handled gracefully to prevent program termination.
What Causes Exceptions?
- Runtime Errors: Division by zero, array index out of bounds
- Resource Issues: File not found, network timeouts
- Input Problems: Invalid user input, data format errors
- System Issues: Out of memory, disk full
- Logic Errors: Null pointer access, invalid operations
Why Handle Exceptions?
- Prevent sudden program termination
- Provide meaningful error messages
- Maintain program stability
- Enable graceful error recovery
- Improve user experience
- Debug and troubleshoot issues
Exception vs Error vs Bug:
| Type | Description | Example |
|---|---|---|
| Exception | Runtime issue, can be handled | FileNotFoundException |
| Error | Serious system problem | OutOfMemoryError |
| Bug | Programming mistake | Wrong algorithm logic |
Example Without Exception Handling:
public class WithoutExceptionHandling {
public static void main(String[] args) {
int[] numbers = {10, 20, 30};
// This will crash the program!
System.out.println(numbers[5]); // Index out of bounds
// This line will never execute
System.out.println("Program continues...");
}
}
// Output:
// Exception in thread "main"
// java.lang.ArrayIndexOutOfBoundsException:
// Index 5 out of bounds for length 3
// Program terminates abruptly!Java Exception Hierarchy
Exception Hierarchy Structure:
Throwable (class)
├── Error (class)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception (class)
├── RuntimeException (unchecked)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── IllegalArgumentException
│ └── NumberFormatException
└── Checked Exceptions
├── IOException
├── ClassNotFoundException
├── SQLException
└── ParseExceptionTypes of Exceptions:
1. Checked Exceptions:
- Must be handled or declared
- Checked at compile time
- Examples: IOException, SQLException
2. Unchecked Exceptions:
- RuntimeException subclasses
- Optional to handle
- Examples: NullPointerException
3. Errors:
- Serious system problems
- Usually not recoverable
- Examples: OutOfMemoryError
// Checked exception example
try {
FileReader file = new FileReader("data.txt");
} catch (FileNotFoundException e) {
System.out.println("File not found!");
}
// Unchecked exception example
String str = null;
try {
int len = str.length(); // NullPointerException
} catch (NullPointerException e) {
System.out.println("String is null!");
}Try-Catch Exception Handling
Basic Try-Catch Syntax:
try {
// Risky code that might throw exception
// Code that could fail
} catch (ExceptionType e) {
// Handle the exception
// Error recovery or logging
}Simple Example:
public class BasicTryCatch {
public static void main(String[] args) {
try {
int result = 10 / 0; // ArithmeticException
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
System.out.println("Exception message: " + e.getMessage());
}
System.out.println("Program continues normally...");
}
}
// Output:
// Error: Cannot divide by zero!
// Exception message: / by zero
// Program continues normally...Multiple Catch Blocks:
public class MultipleCatch {
public static void main(String[] args) {
String[] data = {"10", "20", "abc", "30"};
for (String str : data) {
try {
int number = Integer.parseInt(str);
int result = 100 / number;
System.out.println("100/" + number + " = " + result);
} catch (NumberFormatException e) {
System.out.println("'" + str + "' is not a valid number");
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero for: " + str);
} catch (Exception e) {
System.out.println("Unexpected error: " + e.getMessage());
}
}
}
}
// Output:
// 100/10 = 10
// 100/20 = 5
// 'abc' is not a valid number
// 100/30 = 3Catch Block Order: More specific exceptions must be caught before general ones. The order should be from most specific to most general.
Finally Block
Finally Block Purpose:
The finally block contains code that always executes, regardless of whether an exception occurs or not. It's typically used for cleanup operations.
Finally Block Characteristics:
- Always executes (except System.exit())
- Executes after try or catch blocks
- Used for cleanup operations
- Optional but highly recommended
- Executes even if return statement is in try/catch
Basic Finally Example:
public class FinallyExample {
public static void main(String[] args) {
try {
System.out.println("In try block");
int result = 10 / 0; // Exception occurs
} catch (ArithmeticException e) {
System.out.println("In catch block");
} finally {
System.out.println("In finally block - always executes");
}
System.out.println("After try-catch-finally");
}
}
// Output:
// In try block
// In catch block
// In finally block - always executes
// After try-catch-finallyResource Management with Finally:
import java.io.*;
public class ResourceManagement {
public static void readFile(String filename) {
FileReader file = null;
BufferedReader reader = null;
try {
file = new FileReader(filename);
reader = new BufferedReader(file);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filename);
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
} finally {
// Cleanup resources - always executes
try {
if (reader != null) {
reader.close();
System.out.println("BufferedReader closed");
}
if (file != null) {
file.close();
System.out.println("FileReader closed");
}
} catch (IOException e) {
System.out.println("Error closing resources: " + e.getMessage());
}
}
}
public static void main(String[] args) {
readFile("data.txt"); // File might not exist
readFile("config.txt"); // Another attempt
}
}Common Exception Types and Examples
Runtime Exceptions (Unchecked):
1. NullPointerException:
String str = null;
try {
int length = str.length(); // NPE here
} catch (NullPointerException e) {
System.out.println("String is null!");
}2. ArrayIndexOutOfBoundsException:
int[] numbers = {1, 2, 3};
try {
System.out.println(numbers[5]); // Index 5 doesn't exist
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index out of bounds: " + e.getMessage());
}3. NumberFormatException:
try {
int number = Integer.parseInt("abc123"); // Invalid format
} catch (NumberFormatException e) {
System.out.println("Invalid number format: " + e.getMessage());
}4. IllegalArgumentException:
public void setAge(int age) {
try {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Invalid age: " + age);
}
this.age = age;
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}Checked Exceptions:
1. IOException:
try {
FileInputStream file = new FileInputStream("data.txt");
int data = file.read();
file.close();
} catch (IOException e) {
System.out.println("IO Error: " + e.getMessage());
}2. ClassNotFoundException:
try {
Class clazz = Class.forName("com.example.NonExistentClass");
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + e.getMessage());
}3. ParseException:
import java.text.*;
import java.util.Date;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("invalid-date");
} catch (ParseException e) {
System.out.println("Date parsing error: " + e.getMessage());
}4. InterruptedException:
try {
Thread.sleep(1000); // Sleep for 1 second
} catch (InterruptedException e) {
System.out.println("Thread was interrupted");
Thread.currentThread().interrupt(); // Restore interrupt status
}Exception Handling Best Practices
1. Be Specific with Exceptions:
// ❌ Too general
try {
// some code
} catch (Exception e) {
// catches everything
}
// ✅ Specific exceptions
try {
int result = Integer.parseInt(userInput);
int answer = 100 / result;
} catch (NumberFormatException e) {
System.out.println("Invalid number format");
} catch (ArithmeticException e) {
System.out.println("Division by zero");
}2. Don't Ignore Exceptions:
// ❌ Silent failure
try {
riskyOperation();
} catch (Exception e) {
// Empty catch block - very bad!
}
// ✅ Proper handling
try {
riskyOperation();
} catch (Exception e) {
logger.error("Operation failed", e);
// Take appropriate action
}3. Use Finally for Cleanup:
Connection conn = null;
try {
conn = DriverManager.getConnection(url);
// Database operations
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.out.println("Error closing connection");
}
}
}4. Provide Meaningful Messages:
public void processFile(String filename) {
try {
// File processing code
} catch (FileNotFoundException e) {
throw new RuntimeException(
"Configuration file '" + filename + "' not found. " +
"Please check the file path and try again.", e);
} catch (IOException e) {
throw new RuntimeException(
"Failed to read file '" + filename + "': " + e.getMessage(), e);
}
}Key Best Practices:
- Catch specific exceptions, not general Exception
- Never ignore exceptions (empty catch blocks)
- Use finally blocks for cleanup operations
- Provide meaningful error messages to users
- Log exceptions for debugging purposes
- Don't use exceptions for normal program flow
- Document exceptions in method signatures
Practical Exception Handling Example
Calculator with Exception Handling:
import java.util.Scanner;
public class SafeCalculator {
private Scanner scanner;
public SafeCalculator() {
this.scanner = new Scanner(System.in);
}
public double getNumber(String prompt) {
while (true) {
try {
System.out.print(prompt);
String input = scanner.nextLine().trim();
if (input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be empty");
}
return Double.parseDouble(input);
} catch (NumberFormatException e) {
System.out.println("Error: Please enter a valid number.");
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
public char getOperator() {
while (true) {
try {
System.out.print("Enter operator (+, -, *, /): ");
String input = scanner.nextLine().trim();
if (input.length() != 1) {
throw new IllegalArgumentException("Please enter exactly one operator");
}
char operator = input.charAt(0);
if (operator != '+' && operator != '-' &&
operator != '*' && operator != '/') {
throw new IllegalArgumentException("Invalid operator: " + operator);
}
return operator;
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}Calculator Operations:
public double calculate(double num1, double num2, char operator) {
try {
switch (operator) {
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
if (num2 == 0) {
throw new ArithmeticException("Division by zero is not allowed");
}
return num1 / num2;
default:
throw new IllegalArgumentException("Unsupported operator: " + operator);
}
} catch (ArithmeticException e) {
System.out.println("Math Error: " + e.getMessage());
return Double.NaN; // Not a Number
}
}
public void run() {
System.out.println("=== Safe Calculator ===");
try {
while (true) {
try {
double num1 = getNumber("Enter first number: ");
char operator = getOperator();
double num2 = getNumber("Enter second number: ");
double result = calculate(num1, num2, operator);
if (!Double.isNaN(result)) {
System.out.printf("Result: %.2f %c %.2f = %.2f%n",
num1, operator, num2, result);
}
System.out.print("Continue? (y/n): ");
String continueInput = scanner.nextLine().trim().toLowerCase();
if (!continueInput.equals("y") && !continueInput.equals("yes")) {
break;
}
} catch (Exception e) {
System.out.println("Unexpected error: " + e.getMessage());
System.out.println("Please try again.");
}
}
} finally {
System.out.println("Calculator shutting down...");
scanner.close();
}
}
public static void main(String[] args) {
SafeCalculator calculator = new SafeCalculator();
calculator.run();
}
}Previous Year Exam Questions
Q1. (GTU Summer 2022) What is exception handling in Java? Explain the exception hierarchy with examples. Write a program demonstrating try-catch-finally blocks.
Solution:
Exception Handling in Java:
Exception handling is a mechanism in Java that allows programs to handle runtime errors gracefully, preventing abnormal termination and maintaining program flow. It provides a structured way to catch, handle, and recover from exceptional conditions that may occur during program execution.
Benefits of Exception Handling:
- Program Stability: Prevents crashes and maintains application stability
- Error Recovery: Allows programs to recover from errors and continue execution
- User Experience: Provides meaningful error messages to users
- Debugging: Helps identify and fix problems during development
- Resource Management: Ensures proper cleanup of system resources
Java Exception Hierarchy:
Java exception hierarchy is rooted in the Throwable class, which has two main subclasses:
java.lang.Throwable
├── java.lang.Error
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ ├── VirtualMachineError
│ └── AssertionError
└── java.lang.Exception
├── RuntimeException (Unchecked Exceptions)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── IllegalArgumentException
│ ├── ArithmeticException
│ └── NumberFormatException
└── Checked Exceptions
├── IOException
├── FileNotFoundException
├── SQLException
├── ClassNotFoundException
└── ParseExceptionTypes of Exceptions:
1. Checked Exceptions:
- Must be either caught or declared in method signature
- Checked at compile time
- Represent recoverable conditions
2. Unchecked Exceptions (Runtime Exceptions):
- Optional to handle explicitly
- Usually represent programming errors
- Occur during program execution
3. Errors:
- Serious problems that applications shouldn't try to catch
- Usually indicate system-level problems
- Generally not recoverable
Program Demonstrating Try-Catch-Finally:
import java.io.*;
import java.util.Scanner;
public class ExceptionHandlingDemo {
// Method demonstrating multiple exception types
public static void demonstrateExceptions() {
System.out.println("=== Exception Handling Demonstration ===\n");
// Example 1: Handling multiple exceptions
System.out.println("1. Array and Arithmetic Exception Handling:");
int[] numbers = {10, 20, 30, 0, 50};
for (int i = 0; i <= numbers.length; i++) {
try {
System.out.println("Processing index " + i);
int result = 100 / numbers[i];
System.out.println("100 / " + numbers[i] + " = " + result);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Array index " + i + " is out of bounds");
System.out.println("Array length is: " + numbers.length);
} catch (ArithmeticException e) {
System.out.println("Error: Division by zero at index " + i);
System.out.println("Value at index " + i + " is: " + numbers[i]);
} finally {
System.out.println("Finally block executed for index " + i);
System.out.println("---");
}
}
}
// Method demonstrating file handling with exceptions
public static void demonstrateFileHandling(String filename) {
System.out.println("\n2. File Handling with Exception Management:");
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
System.out.println("Attempting to open file: " + filename);
fileReader = new FileReader(filename);
bufferedReader = new BufferedReader(fileReader);
String line;
int lineNumber = 1;
System.out.println("File contents:");
while ((line = bufferedReader.readLine()) != null) {
System.out.println(lineNumber + ": " + line);
lineNumber++;
}
System.out.println("File read successfully!");
} catch (FileNotFoundException e) {
System.out.println("Error: File '" + filename + "' not found");
System.out.println("Please check if the file exists and path is correct");
} catch (IOException e) {
System.out.println("Error: Unable to read file '" + filename + "'");
System.out.println("IO Error details: " + e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected error occurred: " + e.getClass().getSimpleName());
System.out.println("Error message: " + e.getMessage());
} finally {
// Resource cleanup - always executed
System.out.println("Cleaning up resources...");
try {
if (bufferedReader != null) {
bufferedReader.close();
System.out.println("BufferedReader closed successfully");
}
} catch (IOException e) {
System.out.println("Error closing BufferedReader: " + e.getMessage());
}
try {
if (fileReader != null) {
fileReader.close();
System.out.println("FileReader closed successfully");
}
} catch (IOException e) {
System.out.println("Error closing FileReader: " + e.getMessage());
}
System.out.println("Finally block completed - resources cleaned up");
}
}
// Method demonstrating nested try-catch
public static void demonstrateNestedExceptions() {
System.out.println("\n3. Nested Exception Handling:");
String[] data = {"123", "456", null, "abc", "789"};
for (int i = 0; i < data.length; i++) {
try {
System.out.println("Processing element " + i + ": " + data[i]);
try {
int length = data[i].length(); // Potential NullPointerException
int value = Integer.parseInt(data[i]); // Potential NumberFormatException
int result = 1000 / value; // Potential ArithmeticException
System.out.println("String length: " + length);
System.out.println("Parsed value: " + value);
System.out.println("1000 / " + value + " = " + result);
} catch (NullPointerException e) {
System.out.println("Inner catch: String is null");
throw new IllegalArgumentException("Cannot process null string", e);
} catch (NumberFormatException e) {
System.out.println("Inner catch: Invalid number format");
throw new IllegalArgumentException("Invalid numeric string: " + data[i], e);
}
} catch (IllegalArgumentException e) {
System.out.println("Outer catch: " + e.getMessage());
System.out.println("Caused by: " + e.getCause().getClass().getSimpleName());
} catch (ArithmeticException e) {
System.out.println("Outer catch: Arithmetic error - " + e.getMessage());
} catch (Exception e) {
System.out.println("Outer catch: Unexpected error - " + e.getClass().getSimpleName());
} finally {
System.out.println("Element " + i + " processing completed");
System.out.println("---");
}
}
}
// Interactive demonstration
public static void interactiveDemo() {
System.out.println("\n4. Interactive Exception Handling:");
Scanner scanner = new Scanner(System.in);
try {
while (true) {
try {
System.out.print("Enter a number (or 'quit' to exit): ");
String input = scanner.nextLine().trim();
if (input.equalsIgnoreCase("quit")) {
break;
}
if (input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be empty");
}
double number = Double.parseDouble(input);
double sqrt = Math.sqrt(number);
double reciprocal = 1.0 / number;
System.out.println("Number: " + number);
System.out.println("Square root: " + sqrt);
System.out.println("Reciprocal: " + reciprocal);
System.out.println();
} catch (NumberFormatException e) {
System.out.println("Error: '" + e.getMessage() + "' is not a valid number");
} catch (ArithmeticException e) {
System.out.println("Error: Mathematical operation failed - " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
} catch (Exception e) {
System.out.println("Unexpected error: " + e.getClass().getSimpleName());
}
}
} finally {
System.out.println("Interactive demo completed");
scanner.close();
}
}
public static void main(String[] args) {
try {
// Demonstrate various exception scenarios
demonstrateExceptions();
// Test with existing and non-existing files
demonstrateFileHandling("sample.txt"); // This file may not exist
// Show nested exception handling
demonstrateNestedExceptions();
// Interactive demonstration
interactiveDemo();
System.out.println("\n=== Exception Handling Demo Completed ===");
} catch (Exception e) {
System.out.println("Fatal error in main method: " + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("Program execution finished");
}
}
}Key Concepts Demonstrated:
- Try-Catch-Finally Structure: Proper exception handling flow
- Multiple Catch Blocks: Handling different exception types
- Resource Management: Using finally for cleanup
- Exception Propagation: How exceptions bubble up
- Nested Exception Handling: Try-catch within try-catch
- Exception Information: Getting details from exception objects
Q2. (GTU Winter 2021) Differentiate between checked and unchecked exceptions in Java. Provide examples of each type with appropriate programs.
Solution:
Checked vs Unchecked Exceptions:
| Aspect | Checked Exceptions | Unchecked Exceptions |
|---|---|---|
| Inheritance | Extend Exception class | Extend RuntimeException class |
| Compile Time Checking | Must be handled or declared | Optional to handle |
| When Checked | At compile time | At runtime |
| Purpose | Recoverable conditions | Programming errors |
| Examples | IOException, SQLException | NullPointerException, ArrayIndexOutOfBoundsException |
| Handling Requirement | Mandatory | Optional |
Checked Exceptions Program:
import java.io.*;
import java.sql.*;
import java.text.*;
import java.util.Date;
public class CheckedExceptionDemo {
// Method demonstrating IOException (File operations)
public static void demonstrateIOException() {
System.out.println("=== IOException Demo ===");
// Must handle IOException - it's checked
try {
FileInputStream file = new FileInputStream("nonexistent.txt");
int data = file.read();
file.close();
System.out.println("File read successfully");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO error occurred: " + e.getMessage());
}
}
// Method demonstrating ClassNotFoundException
public static void demonstrateClassNotFoundException() {
System.out.println("\n=== ClassNotFoundException Demo ===");
try {
// Trying to load a class that doesn't exist
Class clazz = Class.forName("com.nonexistent.SomeClass");
System.out.println("Class loaded: " + clazz.getName());
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + e.getMessage());
}
}
// Method demonstrating ParseException
public static void demonstrateParseException() {
System.out.println("\n=== ParseException Demo ===");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String[] dateStrings = {"2023-12-25", "invalid-date", "2023/12/25"};
for (String dateStr : dateStrings) {
try {
Date date = sdf.parse(dateStr);
System.out.println("Parsed date: " + date);
} catch (ParseException e) {
System.out.println("Cannot parse date '" + dateStr + "': " + e.getMessage());
System.out.println("Error at position: " + e.getErrorOffset());
}
}
}
// Method demonstrating InterruptedException
public static void demonstrateInterruptedException() {
System.out.println("\n=== InterruptedException Demo ===");
try {
System.out.println("Thread will sleep for 2 seconds...");
Thread.sleep(2000); // Must handle InterruptedException
System.out.println("Thread woke up normally");
} catch (InterruptedException e) {
System.out.println("Thread was interrupted: " + e.getMessage());
// Restore interrupt status
Thread.currentThread().interrupt();
}
}
// Method that declares checked exception instead of handling
public static void methodThatThrowsCheckedException() throws IOException {
// This method declares that it might throw IOException
// Caller must handle this exception
throw new IOException("Simulated IO exception");
}
public static void main(String[] args) {
System.out.println("=== Checked Exceptions Demonstration ===\n");
demonstrateIOException();
demonstrateClassNotFoundException();
demonstrateParseException();
demonstrateInterruptedException();
// Calling method that declares exception
System.out.println("\n=== Method with declared exception ===");
try {
methodThatThrowsCheckedException();
} catch (IOException e) {
System.out.println("Caught declared exception: " + e.getMessage());
}
System.out.println("\nAll checked exceptions handled successfully!");
}
}Unchecked Exceptions Program:
import java.util.*;
public class UncheckedExceptionDemo {
// Method demonstrating NullPointerException
public static void demonstrateNullPointerException() {
System.out.println("=== NullPointerException Demo ===");
String str = null;
String[] array = {"Hello", null, "World"};
for (int i = 0; i < array.length; i++) {
try {
// This might throw NullPointerException
int length = array[i].length();
System.out.println("String '" + array[i] + "' has length: " + length);
} catch (NullPointerException e) {
System.out.println("Null string found at index " + i);
}
}
}
// Method demonstrating ArrayIndexOutOfBoundsException
public static void demonstrateArrayIndexException() {
System.out.println("\n=== ArrayIndexOutOfBoundsException Demo ===");
int[] numbers = {10, 20, 30, 40, 50};
int[] indices = {0, 2, 4, 7, -1}; // Some invalid indices
for (int index : indices) {
try {
System.out.println("numbers[" + index + "] = " + numbers[index]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid index " + index +
" for array of length " + numbers.length);
}
}
}
// Method demonstrating ArithmeticException
public static void demonstrateArithmeticException() {
System.out.println("\n=== ArithmeticException Demo ===");
int[] dividends = {100, 50, 25};
int[] divisors = {5, 2, 0}; // One divisor is zero
for (int i = 0; i < dividends.length; i++) {
try {
int result = dividends[i] / divisors[i];
System.out.println(dividends[i] + " / " + divisors[i] + " = " + result);
} catch (ArithmeticException e) {
System.out.println("Cannot divide " + dividends[i] +
" by " + divisors[i] + ": " + e.getMessage());
}
}
}
// Method demonstrating NumberFormatException
public static void demonstrateNumberFormatException() {
System.out.println("\n=== NumberFormatException Demo ===");
String[] numberStrings = {"123", "45.67", "abc", "12x34", "999"};
for (String str : numberStrings) {
try {
int number = Integer.parseInt(str);
System.out.println("'" + str + "' parsed as integer: " + number);
} catch (NumberFormatException e) {
System.out.println("'" + str + "' is not a valid integer");
// Try parsing as double
try {
double doubleNumber = Double.parseDouble(str);
System.out.println(" But it can be parsed as double: " + doubleNumber);
} catch (NumberFormatException e2) {
System.out.println(" And it's not a valid double either");
}
}
}
}
// Method demonstrating IllegalArgumentException
public static void demonstrateIllegalArgumentException() {
System.out.println("\n=== IllegalArgumentException Demo ===");
int[] testAges = {25, -5, 150, 200, 45};
for (int age : testAges) {
try {
validateAge(age);
System.out.println("Age " + age + " is valid");
} catch (IllegalArgumentException e) {
System.out.println("Invalid age " + age + ": " + e.getMessage());
}
}
}
// Helper method that throws IllegalArgumentException
public static void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
if (age > 120) {
throw new IllegalArgumentException("Age cannot be greater than 120");
}
}
// Method demonstrating ClassCastException
public static void demonstrateClassCastException() {
System.out.println("\n=== ClassCastException Demo ===");
Object[] objects = {"Hello", 123, 45.67, new ArrayList()};
for (int i = 0; i < objects.length; i++) {
try {
String str = (String) objects[i]; // Might cause ClassCastException
System.out.println("Object " + i + " as String: " + str);
} catch (ClassCastException e) {
System.out.println("Object " + i + " (" + objects[i].getClass().getSimpleName() +
") cannot be cast to String");
}
}
}
public static void main(String[] args) {
System.out.println("=== Unchecked Exceptions Demonstration ===\n");
// These methods may throw unchecked exceptions
// Handling is optional, but recommended for robustness
demonstrateNullPointerException();
demonstrateArrayIndexException();
demonstrateArithmeticException();
demonstrateNumberFormatException();
demonstrateIllegalArgumentException();
demonstrateClassCastException();
System.out.println("\n=== Key Differences Summary ===");
System.out.println("Checked Exceptions:");
System.out.println("- Must be handled or declared");
System.out.println("- Checked at compile time");
System.out.println("- Usually recoverable conditions");
System.out.println("- Examples: IOException, SQLException, ParseException");
System.out.println("\nUnchecked Exceptions:");
System.out.println("- Optional to handle");
System.out.println("- Occur at runtime");
System.out.println("- Usually programming errors");
System.out.println("- Examples: NullPointerException, ArrayIndexOutOfBoundsException");
System.out.println("\nProgram completed successfully!");
}
} Key Differences Explained:
Checked Exceptions:
- Compile-time Enforcement: Must be handled or declared in method signature
- Recoverable: Usually represent conditions the program can recover from
- External Dependencies: Often related to external resources (files, network, database)
- Explicit Handling: Forces developers to think about error handling
Unchecked Exceptions:
- Runtime Detection: Occur during program execution
- Programming Errors: Usually indicate bugs in the code
- Optional Handling: Can be handled but not required
- Prevention: Better to prevent through proper coding practices
Q3. (GTU Summer 2020) Explain the finally block in Java. When is it executed? Write a program showing the use of finally block in resource management.
Solution:
Finally Block in Java:
The finally block is a code block that always executes, regardless of whether an exception occurs or not. It's primarily used for cleanup operations such as closing files, database connections, or releasing system resources.
Characteristics of Finally Block:
- Always Executes: Runs whether exception occurs or not
- Cleanup Operations: Ideal for resource management
- After Try/Catch: Executes after try or catch blocks complete
- Exception Scenarios: Executes even if return statement is in try/catch
- System.exit() Exception: Only way to prevent finally execution
When Finally Block Executes:
- Normal Execution: When try block completes successfully
- Exception Caught: After catch block handles the exception
- Uncaught Exception: Before exception propagates up the call stack
- Return Statement: Even if return is called in try or catch
- Break/Continue: Even with loop control statements
Resource Management Program:
import java.io.*;
import java.sql.*;
import java.net.*;
import java.util.Scanner;
public class FinallyBlockResourceManagement {
// Example 1: File Resource Management
public static void fileResourceManagement(String filename) {
System.out.println("=== File Resource Management with Finally ===");
FileReader fileReader = null;
BufferedReader bufferedReader = null;
FileWriter logWriter = null;
try {
System.out.println("Opening file: " + filename);
fileReader = new FileReader(filename);
bufferedReader = new BufferedReader(fileReader);
// Open log file for writing
logWriter = new FileWriter("access.log", true);
logWriter.write("File accessed: " + filename + " at " +
new java.util.Date() + "\n");
String line;
int lineCount = 0;
while ((line = bufferedReader.readLine()) != null) {
lineCount++;
System.out.println("Line " + lineCount + ": " + line);
// Simulate potential exception during processing
if (line.contains("ERROR")) {
throw new RuntimeException("Error found in file content");
}
}
System.out.println("File processed successfully. Lines read: " + lineCount);
} catch (FileNotFoundException e) {
System.out.println("File not found: " + filename);
} catch (IOException e) {
System.out.println("IO error while reading file: " + e.getMessage());
} catch (RuntimeException e) {
System.out.println("Runtime error: " + e.getMessage());
} finally {
// Resource cleanup - ALWAYS executed
System.out.println("Finally block: Cleaning up resources...");
// Close BufferedReader
if (bufferedReader != null) {
try {
bufferedReader.close();
System.out.println("BufferedReader closed successfully");
} catch (IOException e) {
System.out.println("Error closing BufferedReader: " + e.getMessage());
}
}
// Close FileReader
if (fileReader != null) {
try {
fileReader.close();
System.out.println("FileReader closed successfully");
} catch (IOException e) {
System.out.println("Error closing FileReader: " + e.getMessage());
}
}
// Close log writer
if (logWriter != null) {
try {
logWriter.close();
System.out.println("Log writer closed successfully");
} catch (IOException e) {
System.out.println("Error closing log writer: " + e.getMessage());
}
}
System.out.println("All file resources cleaned up");
}
}
// Example 2: Database Resource Management
public static void databaseResourceManagement() {
System.out.println("\n=== Database Resource Management with Finally ===");
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
System.out.println("Attempting database connection...");
// Simulate database connection (would be real in actual application)
// connection = DriverManager.getConnection("jdbc:mysql://localhost/testdb", "user", "pass");
// For demo, we'll simulate with null and handle it
if (connection == null) {
// Simulate connection creation
System.out.println("Database connection established (simulated)");
// Simulate statement creation
System.out.println("Creating statement...");
// statement = connection.createStatement();
// Simulate query execution
System.out.println("Executing query...");
// resultSet = statement.executeQuery("SELECT * FROM users");
// Simulate processing results
System.out.println("Processing query results...");
// while (resultSet.next()) {
// System.out.println("User: " + resultSet.getString("name"));
// }
// Simulate an exception during processing
throw new SQLException("Simulated database connection timeout");
}
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
System.out.println("Attempting to rollback transaction...");
try {
if (connection != null && !connection.getAutoCommit()) {
connection.rollback();
System.out.println("Transaction rolled back successfully");
}
} catch (SQLException rollbackEx) {
System.out.println("Error during rollback: " + rollbackEx.getMessage());
}
} finally {
// Database resource cleanup - ALWAYS executed
System.out.println("Finally block: Closing database resources...");
// Close ResultSet
if (resultSet != null) {
try {
resultSet.close();
System.out.println("ResultSet closed");
} catch (SQLException e) {
System.out.println("Error closing ResultSet: " + e.getMessage());
}
}
// Close Statement
if (statement != null) {
try {
statement.close();
System.out.println("Statement closed");
} catch (SQLException e) {
System.out.println("Error closing Statement: " + e.getMessage());
}
}
// Close Connection
if (connection != null) {
try {
connection.close();
System.out.println("Database connection closed");
} catch (SQLException e) {
System.out.println("Error closing connection: " + e.getMessage());
}
}
System.out.println("All database resources cleaned up");
}
}
// Example 3: Network Resource Management
public static void networkResourceManagement(String urlString) {
System.out.println("\n=== Network Resource Management with Finally ===");
HttpURLConnection connection = null;
InputStream inputStream = null;
Scanner scanner = null;
try {
System.out.println("Connecting to: " + urlString);
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000); // 5 seconds timeout
connection.setReadTimeout(5000);
int responseCode = connection.getResponseCode();
System.out.println("Response code: " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
inputStream = connection.getInputStream();
scanner = new Scanner(inputStream);
int lineCount = 0;
while (scanner.hasNextLine() && lineCount < 5) { // Read first 5 lines
String line = scanner.nextLine();
System.out.println("Response line " + (++lineCount) + ": " +
line.substring(0, Math.min(line.length(), 50)) + "...");
}
System.out.println("Network request completed successfully");
} else {
throw new IOException("HTTP request failed with code: " + responseCode);
}
} catch (MalformedURLException e) {
System.out.println("Invalid URL: " + e.getMessage());
} catch (IOException e) {
System.out.println("Network error: " + e.getMessage());
} finally {
// Network resource cleanup - ALWAYS executed
System.out.println("Finally block: Cleaning up network resources...");
// Close Scanner
if (scanner != null) {
scanner.close();
System.out.println("Scanner closed");
}
// Close InputStream
if (inputStream != null) {
try {
inputStream.close();
System.out.println("InputStream closed");
} catch (IOException e) {
System.out.println("Error closing InputStream: " + e.getMessage());
}
}
// Disconnect HttpURLConnection
if (connection != null) {
connection.disconnect();
System.out.println("HTTP connection closed");
}
System.out.println("All network resources cleaned up");
}
}
// Example 4: Finally with Return Statements
public static String finallyWithReturn(boolean throwException) {
System.out.println("\n=== Finally Block with Return Statements ===");
try {
System.out.println("In try block");
if (throwException) {
throw new RuntimeException("Simulated exception");
}
System.out.println("About to return from try block");
return "Returned from try block";
} catch (RuntimeException e) {
System.out.println("In catch block: " + e.getMessage());
System.out.println("About to return from catch block");
return "Returned from catch block";
} finally {
// This ALWAYS executes, even with return statements above
System.out.println("Finally block executes even with return statements");
// Note: If we return from finally, it will override the try/catch returns
// return "Returned from finally"; // This would override other returns
}
}
// Example 5: Multiple Finally Blocks (Nested Try-Catch)
public static void nestedTryWithFinally() {
System.out.println("\n=== Nested Try-Catch with Multiple Finally Blocks ===");
try {
System.out.println("Outer try block");
try {
System.out.println("Inner try block");
throw new IllegalArgumentException("Inner exception");
} catch (IllegalArgumentException e) {
System.out.println("Inner catch: " + e.getMessage());
throw new RuntimeException("Exception from inner catch", e);
} finally {
System.out.println("Inner finally block - always executes");
}
} catch (RuntimeException e) {
System.out.println("Outer catch: " + e.getMessage());
} finally {
System.out.println("Outer finally block - always executes");
}
}
public static void main(String[] args) {
System.out.println("=== Finally Block Resource Management Demo ===\n");
// Test file resource management
fileResourceManagement("sample.txt"); // File may not exist
// Test database resource management
databaseResourceManagement();
// Test network resource management
networkResourceManagement("https://httpbin.org/get");
// Demonstrate finally with return statements
String result1 = finallyWithReturn(false);
System.out.println("Result (no exception): " + result1);
String result2 = finallyWithReturn(true);
System.out.println("Result (with exception): " + result2);
// Demonstrate nested try-finally
nestedTryWithFinally();
System.out.println("\n=== Key Points About Finally Block ===");
System.out.println("1. Always executes (except System.exit())");
System.out.println("2. Executes after try or catch blocks");
System.out.println("3. Perfect for resource cleanup");
System.out.println("4. Executes even with return statements");
System.out.println("5. Can have nested finally blocks");
System.out.println("6. Essential for preventing resource leaks");
}
}When Finally Block Does NOT Execute:
- System.exit(): If called in try or catch block
- JVM Crash: If JVM crashes or is killed
- Infinite Loop: If try/catch block has infinite loop
- Thread Death: If thread is killed forcefully
Best Practices for Finally Block:
- Always close resources (files, connections, streams)
- Handle exceptions within finally block
- Keep finally block code simple and fast
- Don't return values from finally block
- Use try-with-resources when possible (Java 7+)
Modern Alternative - Try-with-Resources:
// Java 7+ try-with-resources (automatic resource management)
public static void modernResourceManagement(String filename) {
try (FileReader file = new FileReader(filename);
BufferedReader reader = new BufferedReader(file)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("IO Error: " + e.getMessage());
}
// Resources automatically closed - no finally needed!
}Lecture Summary
Key Concepts Covered:
- Exception concepts and terminology
- Java exception hierarchy structure
- Checked vs unchecked exceptions
- Try-catch-finally block syntax and usage
- Multiple catch blocks and ordering
- Resource management with finally
- Exception handling best practices
Learning Outcomes Achieved:
- ✅ Understand exception handling fundamentals
- ✅ Master try-catch-finally structures
- ✅ Distinguish exception types and handling requirements
- ✅ Implement proper resource management
- ✅ Apply exception handling best practices
- ✅ Create robust and fault-tolerant programs
- ✅ Debug and troubleshoot exception scenarios
Next Lecture: Custom Exceptions and Advanced Exception Handling
Topics: Creating custom exceptions, throw and throws keywords, exception propagation, chained exceptions
Thank You!
Questions & Discussion
Next: Lecture 14 - Custom Exceptions and Advanced Exception Handling
Course: 4343203 Java Programming
Unit 3: Exception Handling and Advanced Topics
GTU Semester 4

