Exception Handling Fundamentals#
Lecture 26#
Java Programming (4343203)
Diploma in ICT - Semester IV
Gujarat Technological University
layout: default#
Learning Objectives#
By the end of this lecture, you will be able to:
- 🚨 Understand the concept of exceptions and error handling in Java
- 🏗️ Explain the exception hierarchy and types of exceptions
- ⚡ Differentiate between errors, checked exceptions, and unchecked exceptions
- 🎯 Recognize common built-in exceptions in Java
- 📝 Understand the importance of exception handling in robust programming
- 🛠️ Identify scenarios where exceptions occur
- 🔍 Analyze exception propagation and stack traces
layout: default#
What are Exceptions?#
Definition#
An exception is an event that occurs during program execution that disrupts the normal flow of program instructions.
Key Characteristics#
- 🚨 Abnormal Events: Represent unexpected situations
- 🔄 Recoverable: Can often be handled gracefully
- 📍 Runtime Events: Occur during program execution
- 🎯 Specific Information: Provide details about what went wrong
- 🛠️ Handleable: Can be caught and managed
Why Exception Handling?#
- Program Stability: Prevent crashes from unexpected errors
- User Experience: Provide meaningful error messages
- Debugging: Help locate and fix problems
- Resource Management: Ensure proper cleanup
- Robustness: Make programs more reliable
Real-world Analogy#
Think of exceptions like traffic problems:
// Normal traffic flow
public void driveToWork() {
startCar(); // ✅ Normal
driveRoute(); // ✅ Normal
parkAtOffice(); // ✅ Normal
}
// With potential problems
public void driveToWorkSafely() {
try {
startCar(); // Could throw CarWontStartException
driveRoute(); // Could throw TrafficJamException
parkAtOffice(); // Could throw NoParkingException
} catch (CarException e) {
callMechanic(); // Handle car problems
} catch (TrafficException e) {
takeAlternateRoute(); // Handle traffic problems
}
}
Exception Examples#
- FileNotFoundException: File doesn’t exist
- ArrayIndexOutOfBoundsException: Invalid array access
- NullPointerException: Accessing null reference
- SQLException: Database operation fails
- NetworkException: Network connection issues
layout: default#
Exception vs Error vs Normal Flow#
Normal Program Flow#
public class NormalFlow {
public static void main(String[] args) {
int a = 10;
int b = 5;
int result = a / b; // Normal division
System.out.println("Result: " + result); // Output: Result: 2
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("Element: " + numbers[2]); // Output: Element: 3
String text = "Hello World";
System.out.println("Length: " + text.length()); // Output: Length: 11
}
}
Exception Scenarios#
public class ExceptionScenarios {
public static void main(String[] args) {
// Division by zero - ArithmeticException
int a = 10;
int b = 0;
int result = a / b; // 🚨 Exception!
// Array index out of bounds
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]); // 🚨 Exception!
// Null pointer access
String text = null;
System.out.println(text.length()); // 🚨 Exception!
// File not found
FileReader file = new FileReader("nonexistent.txt"); // 🚨 Exception!
}
}
What Happens Without Exception Handling?#
Program Crash Example#
public class CrashExample {
public static void main(String[] args) {
System.out.println("Program started");
int[] arr = {1, 2, 3};
System.out.println("Accessing element at index 5...");
// This will crash the program
System.out.println(arr[5]); // 🚨 CRASH!
// This line will never execute
System.out.println("Program ended successfully");
}
}
/* Output:
Program started
Accessing element at index 5...
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:
Index 5 out of bounds for length 3
at CrashExample.main(CrashExample.java:8)
*/
Impact of Unhandled Exceptions#
- Program Termination: Entire program stops abruptly
- Lost Work: User data may be lost
- Poor User Experience: Cryptic error messages
- System Instability: May affect other parts of system
- Debugging Difficulty: Hard to trace the root cause
Benefits of Exception Handling#
- Graceful Recovery: Continue execution where possible
- User-Friendly Messages: Clear error communication
- Resource Cleanup: Proper closing of files, connections
- Logging: Record errors for debugging
- Alternative Actions: Provide fallback options
layout: default#
Exception Hierarchy in Java#
Exception Class Hierarchy#
java.lang.Object
↓
java.lang.Throwable
↓
├── java.lang.Error
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
│
└── java.lang.Exception
├── IOException (Checked)
│ ├── FileNotFoundException
│ ├── EOFException
│ └── SocketException
│
├── SQLException (Checked)
├── ClassNotFoundException (Checked)
│
└── RuntimeException (Unchecked)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── ArithmeticException
├── IllegalArgumentException
└── NumberFormatException
Throwable Class#
public class Throwable {
private String message;
private Throwable cause;
// Constructor
public Throwable(String message) {
this.message = message;
}
// Key methods
public String getMessage() { return message; }
public void printStackTrace() { /* prints stack trace */ }
public StackTraceElement[] getStackTrace() { /* returns trace */ }
public String toString() { return getClass() + ": " + message; }
}
Understanding the Hierarchy#
1. Throwable - Root Class#
- Base class for all exceptions and errors
- Provides common functionality like stack trace
- Has two main subclasses: Error and Exception
2. Error - System Level Problems#
// Examples of Errors (DO NOT catch these)
public class ErrorExamples {
public static void causeOutOfMemoryError() {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // Allocate 1MB
}
// Eventually throws OutOfMemoryError
}
public static void causeStackOverflowError() {
causeStackOverflowError(); // Infinite recursion
// Eventually throws StackOverflowError
}
}
3. Exception - Application Level Problems#
- Represents conditions that applications should handle
- Two categories: Checked and Unchecked
4. Checked Exceptions#
// Must be declared or caught
public void readFile(String filename) throws IOException {
FileReader file = new FileReader(filename); // May throw IOException
// Compiler enforces handling
}
5. Unchecked Exceptions (RuntimeException)#
// No need to declare or catch (but can be)
public int divide(int a, int b) {
return a / b; // May throw ArithmeticException
}
layout: default#
Types of Exceptions#
1. Checked Exceptions#
Must be handled or declared to be thrown.
Characteristics:#
- Compile-time checking: Must handle or declare
- External dependencies: Often related to I/O, networking
- Recoverable: Usually can be handled gracefully
- Explicit handling: Forces developer to consider error cases
Common Checked Exceptions:#
// IOException - File and I/O operations
try {
FileReader file = new FileReader("data.txt");
BufferedReader reader = new BufferedReader(file);
String line = reader.readLine();
} catch (IOException e) {
System.err.println("File operation failed: " + e.getMessage());
}
// SQLException - Database operations
try {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "pass");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
} catch (SQLException e) {
System.err.println("Database error: " + e.getMessage());
}
// ClassNotFoundException - Dynamic class loading
try {
Class<?> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.newInstance();
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e.getMessage());
}
// InterruptedException - Thread operations
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.err.println("Thread interrupted: " + e.getMessage());
}
2. Unchecked Exceptions (Runtime)#
Optional to handle - occur during runtime.
Characteristics:#
- Runtime checking: Occur during execution
- Programming errors: Often indicate bugs
- Optional handling: No compile-time requirement
- Should be prevented: Better to fix the root cause
Common Unchecked Exceptions:#
// NullPointerException - Most common runtime exception
String text = null;
int length = text.length(); // 🚨 NullPointerException
// ArithmeticException - Mathematical errors
int result = 10 / 0; // 🚨 ArithmeticException
// ArrayIndexOutOfBoundsException - Invalid array access
int[] numbers = {1, 2, 3};
int value = numbers[5]; // 🚨 ArrayIndexOutOfBoundsException
// IllegalArgumentException - Invalid method arguments
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Invalid age: " + age);
}
this.age = age;
}
// NumberFormatException - Invalid string to number conversion
int number = Integer.parseInt("abc"); // 🚨 NumberFormatException
// ClassCastException - Invalid type casting
Object obj = "Hello";
Integer num = (Integer) obj; // 🚨 ClassCastException
Prevention Strategies:#
// Prevent NullPointerException
if (text != null) {
int length = text.length(); // Safe
}
// Prevent ArrayIndexOutOfBoundsException
if (index >= 0 && index < array.length) {
int value = array[index]; // Safe
}
// Prevent NumberFormatException
try {
int number = Integer.parseInt(input);
} catch (NumberFormatException e) {
// Handle invalid input
number = 0; // Default value
}
layout: default#
Common Built-in Exceptions#
Runtime Exceptions (Unchecked)#
1. NullPointerException#
public class NullPointerExamples {
public static void demonstrateNPE() {
String str = null;
// Various ways NPE can occur
int length = str.length(); // 🚨 NPE
String upper = str.toUpperCase(); // 🚨 NPE
boolean empty = str.isEmpty(); // 🚨 NPE
char first = str.charAt(0); // 🚨 NPE
// Object method calls on null
Object obj = null;
String result = obj.toString(); // 🚨 NPE
// Array of objects
String[] arr = new String[5];
int len = arr[0].length(); // 🚨 NPE (array elements are null)
// Method chaining
String text = getText().trim().toUpperCase(); // 🚨 NPE if getText() returns null
}
// Prevention techniques
public static void preventNPE() {
String str = getValue();
// Null check
if (str != null) {
int length = str.length(); // Safe
}
// Using Objects utility
int length = Objects.requireNonNull(str).length();
// Optional (Java 8+)
Optional<String> optional = Optional.ofNullable(str);
optional.ifPresent(s -> System.out.println(s.length()));
}
}
2. ArrayIndexOutOfBoundsException#
public class ArrayIndexExamples {
public static void demonstrateAIOBE() {
int[] numbers = {10, 20, 30, 40, 50};
// Valid indices: 0, 1, 2, 3, 4
System.out.println(numbers[0]); // ✅ Valid: 10
System.out.println(numbers[4]); // ✅ Valid: 50
// Invalid indices
System.out.println(numbers[-1]); // 🚨 AIOBE: negative index
System.out.println(numbers[5]); // 🚨 AIOBE: index >= length
System.out.println(numbers[10]); // 🚨 AIOBE: way out of bounds
// Dynamic access
Scanner input = new Scanner(System.in);
int index = input.nextInt();
System.out.println(numbers[index]); // 🚨 Potential AIOBE
}
// Prevention
public static void preventAIOBE() {
int[] numbers = {10, 20, 30, 40, 50};
int index = getUserInput();
// Bounds checking
if (index >= 0 && index < numbers.length) {
System.out.println(numbers[index]); // Safe
} else {
System.out.println("Invalid index: " + index);
}
// Using enhanced for loop (no index needed)
for (int number : numbers) {
System.out.println(number); // Safe
}
}
}
3. ArithmeticException#
public class ArithmeticExamples {
public static void demonstrateAE() {
// Division by zero
int a = 10;
int b = 0;
int result = a / b; // 🚨 ArithmeticException
int mod = a % b; // 🚨 ArithmeticException
// Note: Floating point division doesn't throw exception
double da = 10.0;
double db = 0.0;
double dresult = da / db; // Result: Infinity (no exception)
}
// Prevention
public static void preventAE() {
int a = 10;
int b = getUserInput();
if (b != 0) {
int result = a / b; // Safe
System.out.println("Result: " + result);
} else {
System.out.println("Cannot divide by zero");
}
// Using try-catch
try {
int result = a / b;
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Division by zero attempted");
}
}
}
4. IllegalArgumentException#
public class IllegalArgumentExamples {
private int age;
private String email;
// Method that validates arguments
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException(
"Age must be between 0 and 150, got: " + age);
}
this.age = age;
}
public void setEmail(String email) {
if (email == null || email.trim().isEmpty()) {
throw new IllegalArgumentException(
"Email cannot be null or empty");
}
if (!email.contains("@")) {
throw new IllegalArgumentException(
"Invalid email format: " + email);
}
this.email = email;
}
// String and collection methods that throw IAE
public static void stringExamples() {
String text = "Hello World";
// substring with invalid indices
String sub1 = text.substring(-1); // 🚨 IllegalArgumentException
String sub2 = text.substring(5, 2); // 🚨 IllegalArgumentException
// Thread.sleep with negative argument
Thread.sleep(-1000); // 🚨 IllegalArgumentException
}
}
layout: default#
Exception Propagation and Stack Traces#
Exception Propagation#
When an exception is thrown, it propagates up the call stack until it’s caught or reaches the main method.
Call Stack Example#
public class PropagationExample {
public static void main(String[] args) {
System.out.println("Main method starts");
try {
methodA();
} catch (ArithmeticException e) {
System.out.println("Caught in main: " + e.getMessage());
}
System.out.println("Main method ends");
}
public static void methodA() {
System.out.println("Method A starts");
methodB();
System.out.println("Method A ends"); // Won't execute if exception
}
public static void methodB() {
System.out.println("Method B starts");
methodC();
System.out.println("Method B ends"); // Won't execute if exception
}
public static void methodC() {
System.out.println("Method C starts");
int result = 10 / 0; // 🚨 ArithmeticException thrown here
System.out.println("Method C ends"); // Won't execute
}
}
/* Output:
Main method starts
Method A starts
Method B starts
Method C starts
Caught in main: / by zero
Main method ends
*/
Propagation Flow#
- Exception occurs in methodC()
- methodC() terminates immediately
- Exception propagates to methodB()
- methodB() terminates without completing
- Exception propagates to methodA()
- methodA() terminates without completing
- Exception propagates to main()
- main() catches the exception and handles it
Understanding Stack Traces#
A stack trace shows the sequence of method calls that led to an exception.
Stack Trace Example#
public class StackTraceExample {
public static void main(String[] args) {
calculateTotal();
}
public static void calculateTotal() {
processData();
}
public static void processData() {
String[] data = {"10", "20", "abc", "30"};
for (int i = 0; i < data.length; i++) {
int value = parseInt(data[i]);
System.out.println("Parsed: " + value);
}
}
public static int parseInt(String str) {
return Integer.parseInt(str); // Exception on "abc"
}
}
/* Stack Trace Output:
Exception in thread "main" java.lang.NumberFormatException: For input string: "abc"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:660)
at java.base/java.lang.Integer.parseInt(Integer.java:778)
at StackTraceExample.parseInt(StackTraceExample.java:15)
at StackTraceExample.processData(StackTraceExample.java:11)
at StackTraceExample.calculateTotal(StackTraceExample.java:6)
at StackTraceExample.main(StackTraceExample.java:3)
*/
Reading Stack Traces#
// Exception type and message
NumberFormatException: For input string: "abc"
// Stack trace (bottom to top shows call sequence)
at StackTraceExample.parseInt(StackTraceExample.java:15) ← Exception origin
at StackTraceExample.processData(StackTraceExample.java:11) ← Called parseInt
at StackTraceExample.calculateTotal(StackTraceExample.java:6) ← Called processData
at StackTraceExample.main(StackTraceExample.java:3) ← Called calculateTotal
Analyzing Stack Traces#
- Exception Type: What kind of exception occurred
- Error Message: Specific details about the problem
- Method Sequence: Chain of method calls leading to exception
- Line Numbers: Exact location where exception occurred
- File Names: Source files involved
layout: default#
Hands-on Exercise 1: Exception Identification#
Exercise: Identify Exception Types#
Analyze the following code snippets and predict what exceptions will occur:
// Snippet 1
public class ExceptionQuiz {
public static void snippet1() {
String[] names = {"Alice", "Bob", "Charlie"};
for (int i = 0; i <= names.length; i++) {
System.out.println("Name: " + names[i]);
}
}
// Snippet 2
public static void snippet2() {
String text = null;
if (text.equals("Hello")) {
System.out.println("Found Hello");
}
}
// Snippet 3
public static void snippet3() {
int[] numbers = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += numbers[i];
}
System.out.println("Sum: " + sum);
}
// Snippet 4
public static void snippet4() {
String input = "12.34abc";
int value = Integer.parseInt(input);
System.out.println("Value: " + value);
}
// Snippet 5
public static void snippet5() {
List<String> list = new ArrayList<>();
list.add("Item1");
list.add("Item2");
String item = list.get(5);
System.out.println("Item: " + item);
}
}
Questions:
- What exception will snippet1() throw and why?
- What exception will snippet2() throw and why?
- What exception will snippet3() throw and why?
- What exception will snippet4() throw and why?
- What exception will snippet5() throw and why?
Solutions and Explanations#
Snippet 1: ArrayIndexOutOfBoundsException#
// Problem: Loop condition uses <= instead of <
for (int i = 0; i <= names.length; i++) // i goes 0,1,2,3
System.out.println("Name: " + names[i]); // names[3] doesn't exist
Fix:
for (int i = 0; i < names.length; i++) // Correct condition
Snippet 2: NullPointerException#
// Problem: Calling method on null reference
String text = null;
if (text.equals("Hello")) // text is null, can't call equals()
Fix:
if ("Hello".equals(text)) // Use string literal first
// or
if (text != null && text.equals("Hello")) // Null check
Snippet 3: ArrayIndexOutOfBoundsException#
// Problem: Loop starts from 1, but array is 0-indexed
for (int i = 1; i <= 5; i++) // i goes 1,2,3,4,5
sum += numbers[i]; // numbers[5] doesn't exist (only 0-4)
Fix:
for (int i = 0; i < numbers.length; i++) // Start from 0
Snippet 4: NumberFormatException#
// Problem: "12.34abc" is not a valid integer
int value = Integer.parseInt("12.34abc"); // Can't parse "abc" part
Fix:
// Use Double.parseDouble() or clean the string first
String cleaned = input.replaceAll("[^0-9]", ""); // Remove non-digits
Snippet 5: IndexOutOfBoundsException#
// Problem: List has only 2 elements (indices 0,1), but accessing index 5
String item = list.get(5); // Index 5 doesn't exist
Fix:
if (index >= 0 && index < list.size()) {
String item = list.get(index);
}
layout: default#
Hands-on Exercise 2: Exception Analysis#
Exercise: Banking System#
Create a simple banking system and identify potential exceptions:
public class BankAccount {
private String accountNumber;
private double balance;
private String ownerName;
public BankAccount(String accountNumber, String ownerName) {
this.accountNumber = accountNumber;
this.ownerName = ownerName;
this.balance = 0.0;
}
// Deposit money
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited: $" + amount);
System.out.println("New balance: $" + balance);
}
// Withdraw money
public void withdraw(double amount) {
balance -= amount;
System.out.println("Withdrew: $" + amount);
System.out.println("New balance: $" + balance);
}
// Transfer money to another account
public void transfer(BankAccount toAccount, double amount) {
this.withdraw(amount);
toAccount.deposit(amount);
System.out.println("Transferred $" + amount +
" to " + toAccount.getOwnerName());
}
// Calculate interest (annual percentage rate)
public double calculateInterest(double rate, int years) {
double interest = balance * (rate / 100) * years;
return interest;
}
public String getOwnerName() { return ownerName; }
public double getBalance() { return balance; }
}
Tasks:
- Identify at least 5 potential exception scenarios
- What inputs could cause problems?
- What validation should be added?
Solution: Potential Exception Scenarios#
1. Constructor Issues#
// NullPointerException scenarios
BankAccount account1 = new BankAccount(null, "John"); // Null account number
BankAccount account2 = new BankAccount("123", null); // Null owner name
// Later usage issues
String owner = account2.getOwnerName().toUpperCase(); // NPE!
2. Deposit Method Issues#
// IllegalArgumentException scenarios
account.deposit(-100); // Negative deposit
account.deposit(0); // Zero deposit
account.deposit(Double.NaN); // Invalid number
account.deposit(Double.POSITIVE_INFINITY); // Infinite amount
3. Withdraw Method Issues#
// ArithmeticException or logic error scenarios
account.withdraw(1000); // Withdraw more than balance (overdraft)
account.withdraw(-50); // Negative withdrawal
4. Transfer Method Issues#
// NullPointerException
account1.transfer(null, 100); // Null destination account
// Cascading exceptions
account1.transfer(account2, -100); // Negative transfer
account1.transfer(account2, 5000); // Insufficient funds
5. Interest Calculation Issues#
// ArithmeticException or invalid results
double interest = account.calculateInterest(-5, 10); // Negative rate
double interest = account.calculateInterest(100, -2); // Negative years
double interest = account.calculateInterest(Double.NaN, 5); // Invalid rate
Improved Version with Validation#
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
if (Double.isNaN(amount) || Double.isInfinite(amount)) {
throw new IllegalArgumentException("Invalid amount: " + amount);
}
balance += amount;
}
public void transfer(BankAccount toAccount, double amount) {
if (toAccount == null) {
throw new IllegalArgumentException("Destination account cannot be null");
}
if (amount <= 0) {
throw new IllegalArgumentException("Transfer amount must be positive");
}
if (balance < amount) {
throw new IllegalArgumentException("Insufficient funds");
}
this.withdraw(amount);
toAccount.deposit(amount);
}
layout: default#
Real-world Applications#
1. File Processing System#
public class FileProcessor {
public void processFile(String filename) {
// Potential Exceptions:
// - FileNotFoundException: File doesn't exist
// - SecurityException: No permission to read
// - IOException: Disk error, network failure
FileReader reader = new FileReader(filename);
BufferedReader buffer = new BufferedReader(reader);
String line;
while ((line = buffer.readLine()) != null) {
processLine(line);
}
buffer.close();
reader.close();
}
private void processLine(String line) {
// Potential Exceptions:
// - NullPointerException: Line is null
// - NumberFormatException: Invalid number format
// - ArrayIndexOutOfBoundsException: Missing data fields
String[] parts = line.split(",");
int id = Integer.parseInt(parts[0]);
String name = parts[1];
double salary = Double.parseDouble(parts[2]);
System.out.println("Employee: " + name + ", Salary: $" + salary);
}
}
2. Database Connection#
public class DatabaseManager {
public void connectAndQuery() {
// Potential Exceptions:
// - ClassNotFoundException: JDBC driver not found
// - SQLException: Connection failed, invalid query
// - SecurityException: Database access denied
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb",
"username", "password");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println("User: " + rs.getString("name"));
}
}
}
3. Web Service Communication#
public class WebServiceClient {
public String fetchData(String url) {
// Potential Exceptions:
// - MalformedURLException: Invalid URL format
// - IOException: Network timeout, connection refused
// - ProtocolException: HTTP protocol error
// - SecurityException: Access denied
URL serviceUrl = new URL(url);
HttpURLConnection connection =
(HttpURLConnection) serviceUrl.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000); // 5 second timeout
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
connection.disconnect();
return response.toString();
}
}
4. User Input Validation#
public class UserInputProcessor {
private Scanner scanner = new Scanner(System.in);
public int getValidAge() {
// Potential Exceptions:
// - NumberFormatException: Non-numeric input
// - InputMismatchException: Wrong data type
// - NoSuchElementException: No more input available
System.out.print("Enter your age: ");
String input = scanner.nextLine();
int age = Integer.parseInt(input); // Can throw NFE
// Business logic validation
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
return age;
}
public String getValidEmail() {
System.out.print("Enter email: ");
String email = scanner.nextLine();
// Potential NullPointerException if email is null
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
return email;
}
}
layout: default#
Exception Handling Best Practices#
Do’s and Don’ts#
✅ DO’s#
1. Handle Specific Exceptions#
// GOOD: Handle specific exceptions differently
try {
processFile(filename);
} catch (FileNotFoundException e) {
System.err.println("File not found: " + filename);
// Maybe create default file or use alternative
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
// Maybe retry or log error
} catch (SecurityException e) {
System.err.println("Access denied: " + filename);
// Request permissions or use alternative
}
2. Provide Meaningful Error Messages#
// GOOD: Descriptive error messages
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative. Received: " + age);
}
if (age > 150) {
throw new IllegalArgumentException(
"Age cannot exceed 150. Received: " + age);
}
this.age = age;
}
3. Log Exceptions for Debugging#
// GOOD: Log exceptions with context
try {
processUserData(userData);
} catch (DataValidationException e) {
Logger.getLogger(getClass()).error(
"Data validation failed for user: " + userData.getId(), e);
throw e; // Re-throw if needed
}
4. Clean Up Resources#
// GOOD: Always clean up resources
FileInputStream fis = null;
try {
fis = new FileInputStream("data.txt");
// Process file
} catch (IOException e) {
System.err.println("File processing error: " + e.getMessage());
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.err.println("Error closing file: " + e.getMessage());
}
}
}
❌ DON’Ts#
1. Don’t Catch Generic Exception#
// BAD: Too generic, hides different error types
try {
processFile(filename);
} catch (Exception e) {
System.out.println("Something went wrong");
// Can't tell what actually happened
}
2. Don’t Ignore Exceptions#
// BAD: Silent failure
try {
int result = Integer.parseInt(userInput);
} catch (NumberFormatException e) {
// Ignoring exception - very bad!
}
// BAD: Empty catch block
try {
riskyOperation();
} catch (Exception e) {
// TODO: Handle exception - never gets done!
}
3. Don’t Use Exceptions for Control Flow#
// BAD: Using exceptions for normal logic
public boolean isValidNumber(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException e) {
return false; // Using exception for control flow
}
}
// GOOD: Proper validation
public boolean isValidNumber(String str) {
if (str == null || str.isEmpty()) return false;
for (char c : str.toCharArray()) {
if (!Character.isDigit(c)) return false;
}
return true;
}
4. Don’t Catch and Re-throw Without Purpose#
// BAD: Unnecessary catch and re-throw
public void processData() throws IOException {
try {
readDataFile();
} catch (IOException e) {
throw e; // Why catch if just re-throwing?
}
}
// GOOD: Add value when catching and re-throwing
public void processData() throws DataProcessingException {
try {
readDataFile();
} catch (IOException e) {
throw new DataProcessingException(
"Failed to process data file", e);
}
}
layout: default#
Summary and Key Takeaways#
What We Learned#
- 🚨 Exception Fundamentals: Abnormal events that disrupt program flow
- 🏗️ Exception Hierarchy: Throwable → Error/Exception → Checked/Unchecked
- ⚡ Exception Types: Checked (compile-time) vs Unchecked (runtime)
- 🎯 Common Exceptions: NPE, AIOBE, ArithmeticException, etc.
- 📝 Exception Propagation: How exceptions travel up the call stack
- 🛠️ Stack Traces: Reading and understanding error information
- 🔍 Best Practices: Specific handling, meaningful messages, proper cleanup
Exception Handling Benefits#
For Developers#
- Debugging: Easier to identify and fix problems
- Code Quality: More robust and reliable programs
- Maintenance: Cleaner error handling logic
- Testing: Better error scenario coverage
For Users#
- Better Experience: Graceful error recovery
- Clear Messages: Understanding what went wrong
- Data Safety: Prevention of data loss
- System Stability: Continued operation after errors
Key Principles Recap#
- Fail Fast: Detect problems early
- Fail Gracefully: Handle errors without crashing
- Specific Handling: Catch specific exception types
- Meaningful Messages: Provide clear error information
- Resource Cleanup: Always clean up properly
- Don’t Ignore: Never swallow exceptions silently
- Log Everything: Record errors for debugging
- Prevention First: Validate input to prevent exceptions
Real-world Impact#
Without Exception Handling#
- Programs crash unexpectedly
- Data gets corrupted or lost
- Users see cryptic error messages
- Systems become unreliable
- Debugging becomes very difficult
With Proper Exception Handling#
- Graceful error recovery
- Data integrity maintained
- User-friendly error messages
- System remains stable
- Easy debugging and maintenance
Next Steps#
In the following lectures, we’ll learn:
- Try-Catch-Finally: Actual exception handling syntax
- Throw/Throws: Creating and declaring exceptions
- Custom Exceptions: Building your own exception types
- Best Practices: Advanced exception handling patterns
layout: center class: text-center#
Thank You!#
Exception Handling Fundamentals Complete#
Lecture 26 Successfully Completed!
Understanding exceptions is crucial for writing robust Java programs

