Exception Handling

Introduction

Exception is an abnormal event that disrupts normal program flow. Exception handling provides a way to handle runtime errors gracefully.


What is Exception?

Exception = unexpected problem during program execution

Examples:

  • Dividing by zero
  • File not found
  • Array index out of bounds
  • Null pointer access
  • Network connection failure

Without Exception Handling

public class Main {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int result = a / b;  // Runtime error: ArithmeticException
        System.out.println(result);  // Never executes
        System.out.println("Program end");  // Never executes
    }
}

Output:

Exception in thread "main" java.lang.ArithmeticException: / by zero

Program crashes and stops.


With Exception Handling

public class Main {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 0;
            int result = a / b;  // Exception occurs
            System.out.println(result);
        } catch (ArithmeticException e) {
            System.out.println("Cannot divide by zero!");
        }
        System.out.println("Program continues...");  // Executes!
    }
}

Output:

Cannot divide by zero!
Program continues...

Program handles error and continues.


Exception Handling Keywords

KeywordPurpose
tryCode that may throw exception
catchHandle the exception
finallyAlways executes (cleanup)
throwManually throw exception
throwsDeclare exceptions method can throw

try-catch Syntax

try {
    // Code that may throw exception
} catch (ExceptionType e) {
    // Handle exception
}

Simple Examples

ArithmeticException:

public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
            System.out.println(result);
        } catch (ArithmeticException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

NullPointerException:

public class Main {
    public static void main(String[] args) {
        try {
            String str = null;
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("String is null!");
        }
    }
}

ArrayIndexOutOfBoundsException:

public class Main {
    public static void main(String[] args) {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[5]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Invalid index!");
        }
    }
}

Multiple catch Blocks

public class Main {
    public static void main(String[] args) {
        try {
            int[] arr = {1, 2, 3};
            System.out.println(arr[5]);  // May throw ArrayIndexOutOfBoundsException
            int result = 10 / 0;         // May throw ArithmeticException
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Invalid array index");
        } catch (ArithmeticException e) {
            System.out.println("Cannot divide by zero");
        } catch (Exception e) {
            System.out.println("Some error occurred");
        }
        System.out.println("Program continues");
    }
}

Rule: Specific exceptions before general Exception.


Multi-catch (Java 7+)

Handle multiple exceptions in one catch block.

public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException | NullPointerException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Exception Object Methods

public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            // getMessage() - error message
            System.out.println("Message: " + e.getMessage());

            // toString() - exception type + message
            System.out.println("ToString: " + e.toString());

            // printStackTrace() - detailed trace
            e.printStackTrace();
        }
    }
}

Nested try-catch

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Outer try");

            try {
                int result = 10 / 0;
            } catch (ArithmeticException e) {
                System.out.println("Inner catch: " + e.getMessage());
            }

            String str = null;
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("Outer catch: " + e.getMessage());
        }
    }
}

Complete Example: Calculator

import java.util.Scanner;

public class Calculator {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        try {
            System.out.print("Enter first number: ");
            int num1 = sc.nextInt();

            System.out.print("Enter second number: ");
            int num2 = sc.nextInt();

            System.out.print("Enter operation (+, -, *, /): ");
            char op = sc.next().charAt(0);

            int result = 0;

            switch (op) {
                case '+':
                    result = num1 + num2;
                    break;
                case '-':
                    result = num1 - num2;
                    break;
                case '*':
                    result = num1 * num2;
                    break;
                case '/':
                    result = num1 / num2;  // May throw ArithmeticException
                    break;
                default:
                    System.out.println("Invalid operation");
                    return;
            }

            System.out.println("Result: " + result);

        } catch (ArithmeticException e) {
            System.out.println("Error: Cannot divide by zero");
        } catch (Exception e) {
            System.out.println("Error: Invalid input");
        } finally {
            sc.close();
            System.out.println("Calculator closed");
        }
    }
}

Exception Propagation

Exception moves up the call stack until caught.

class Demo {
    void method3() {
        int result = 10 / 0;  // Exception occurs here
    }

    void method2() {
        method3();  // Exception propagates
    }

    void method1() {
        try {
            method2();  // Exception propagates
        } catch (ArithmeticException e) {
            System.out.println("Exception caught in method1");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Demo d = new Demo();
        d.method1();
        System.out.println("Program continues");
    }
}

Benefits of Exception Handling

  1. Prevents crash - program continues
  2. Separates error handling from normal code
  3. Provides error information
  4. Maintains program flow
  5. Cleanup resources properly

try-finally (without catch)

import java.io.*;

public class Main {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("test.txt");
            // Read file
        } finally {
            // Always close file
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Real-World Example: File Reading

import java.io.*;

public class FileReader {
    public static void main(String[] args) {
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new java.io.FileReader("data.txt"));
            String line;

            System.out.println("File contents:");
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

        } catch (FileNotFoundException e) {
            System.out.println("Error: File not found");
        } catch (IOException e) {
            System.out.println("Error: Cannot read file");
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                    System.out.println("File closed");
                }
            } catch (IOException e) {
                System.out.println("Error closing file");
            }
        }
    }
}

Exception Flow

1. Exception occurs in try block

2. Execution stops at that point

3. JVM searches for matching catch

4. If found: execute catch block
   If not found: propagate to caller

5. Execute finally block (if present)

6. Continue after try-catch-finally

Common Exceptions

ExceptionCause
ArithmeticExceptionDivision by zero
NullPointerExceptionUsing null reference
ArrayIndexOutOfBoundsExceptionInvalid array index
NumberFormatExceptionInvalid string to number
FileNotFoundExceptionFile doesn’t exist
IOExceptionI/O operation failed
ClassNotFoundExceptionClass not found
IllegalArgumentExceptionInvalid method argument

Exception Hierarchy (Brief)

Object
  └── Throwable
       ├── Error (serious, don't catch)
       └── Exception
            ├── RuntimeException (unchecked)
            └── Other Exceptions (checked)

Best Practices

  1. Catch specific exceptions first
  2. Don’t catch Error class
  3. Always close resources in finally
  4. Log exceptions for debugging
  5. Don’t hide exceptions with empty catch
  6. Use meaningful error messages
  7. Clean up resources properly

Common Mistakes

// ✗ Wrong - empty catch block
try {
    int result = 10 / 0;
} catch (Exception e) {
    // Nothing - bad practice!
}

// ✗ Wrong - general before specific
try {
    // code
} catch (Exception e) {
    // ...
} catch (ArithmeticException e) {  // Error: unreachable
    // ...
}

// ✓ Correct - specific first
try {
    // code
} catch (ArithmeticException e) {
    System.out.println("Arithmetic error");
} catch (Exception e) {
    System.out.println("General error");
}

Quick Reference

// Basic try-catch
try {
    // risky code
} catch (ExceptionType e) {
    // handle
}

// Multiple catch
try {
    // risky code
} catch (Exception1 e) {
    // handle 1
} catch (Exception2 e) {
    // handle 2
}

// Multi-catch (Java 7+)
try {
    // risky code
} catch (Exception1 | Exception2 e) {
    // handle both
}

// try-catch-finally
try {
    // risky code
} catch (Exception e) {
    // handle
} finally {
    // always runs
}

// Exception methods
e.getMessage()      // error message
e.toString()        // exception info
e.printStackTrace() // detailed trace

When to Use Exception Handling?

Use for:

  • File operations
  • Network operations
  • Database operations
  • User input validation
  • Array/collection access
  • Type conversions

Don’t use for:

  • Normal program logic
  • Expected conditions
  • Control flow

Exam Tips

Remember:

  1. try contains risky code
  2. catch handles exception
  3. finally always executes
  4. Specific exceptions before general
  5. Multiple catch blocks allowed
  6. Multi-catch with | (Java 7+)
  7. Exception object has methods
  8. Propagation up call stack
  9. Don’t catch Error class
  10. Always close resources in finally

Common Questions:

  • What is exception?
  • Purpose of exception handling?
  • try-catch syntax?
  • Multiple catch blocks?
  • Order of catch blocks?
  • What is finally block?
  • Exception propagation?
  • Common exception types?
  • Benefits of exception handling?
  • Best practices?