Exception Handling

Exception Handling

Introduction to Errors & Exceptions
  • An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. It is a signal that indicates a condition that requires special attention.

  • When an exception occurs, the normal execution of the program is halted, and the system attempts to find an exception handler to deal with the situation.

  • An exception is an error event or exceptional condition during program execution that prevents normal continuation.

  • It's an object automatically created by Java when an error is detected, containing information about the error.

  • Exception handling is a mechanism that allows programs to manage exceptions gracefully, preventing abrupt crashes by providing ways to detect and resolve errors that occur during runtime. This involves the use of try-catch blocks to handle exceptions and ensure that the program can recover or terminate safely.

  • Exception handling allows programs to manage exceptions and recover, preventing crashes.

  • Java addresses errors by dealing with exceptions.

  • Exceptions arise from:

    • Numeric overflow errors: Occur when the result of an arithmetic operation exceeds the maximum value that can be stored in the variable.

    • Type conversion errors: Happen when there is an attempt to convert a value of one data type into another type that is not supported or compatible.

    • Dividing by zero errors: Occur when a number is divided by zero, which is mathematically undefined and results in an ArithmeticException.

    • Trying to open a non-existent file: Attempting to open a file that does not exist or cannot be found in the specified location results in a FileNotFoundException.

    • Running out of memory: Occurs when the program requires more memory than is available, leading to an OutOfMemoryError.

    • Database and SQL issues: Problems that arise during database interactions, such as incorrect SQL syntax, connection errors, or data integrity violations.

    • Array index out of bounds: Occurs when trying to access an array element with an index that is outside the valid range (i.e., less than 0 or greater than or equal to the array length), resulting in an ArrayIndexOutOfBoundsException.

    • Trying to use a null object or reference: Attempting to access the methods or properties of an object that has a null reference leads to a NullPointerException.

Common Exception Scenarios
  • File handling or database interactions commonly lead to exceptions.

    • A needed file cannot be found. When a program attempts to open or access a file that does not exist in the specified location, a FileNotFoundException is thrown.

    • Lack of permissions to write to a database or file. Attempting to write data to a file or database without the necessary permissions results in a security exception or an IOException.

    • File or data field with the wrong type. When the data in a file or database field does not match the expected data type, a ClassCastException or NumberFormatException may occur.

  • Exceptions can occur when using classes, potentially due to incorrect usage. This can include passing invalid arguments to methods, calling methods in an inappropriate sequence, or attempting to perform operations that are not supported by the class.

  • Incorrect usage should result in an exception. When developers misuse a class or its methods, exceptions are thrown to indicate that the class is being used in a way that it was not designed to handle.

Importance of Exception Handling
  • Run-time errors cause program crashes. When a program encounters an unhandled exception during runtime, it typically terminates abruptly, leading to a crash.

  • Software failure is unacceptable. In many applications, software failure can have severe consequences, ranging from data loss to system instability or even endangering human lives.

  • Software should handle all possible run-time situations. Robust software should anticipate and handle potential runtime errors gracefully, ensuring that the program can continue to operate correctly or terminate safely without causing harm.

Approaches to Error-Checking
  1. Ignore all but the most important errors.

    • Cleaner code, but misbehavior upon encountering unusual errors. While ignoring less critical errors can lead to cleaner and more concise code, it may also result in unexpected behavior or incorrect results when the program encounters unusual or unforeseen error conditions.

  2. Address every error appropriately.

    • Cluttered code, but improved program reliability. However, some error conditions might be missed. Addressing every possible error scenario can result in more verbose and complex code, but it also improves the program's reliability and robustness. However, developers may still overlook certain error conditions or fail to handle them correctly.

  3. Separate normal processing from error handling.

    • Preferred approach. Designing software with clear separation between normal processing logic and error-handling code is generally the preferred approach. This makes the code easier to understand, maintain, and debug.

    • Reasonably uncluttered code. By separating error-handling code from normal processing, the codebase remains clean and organized, enhancing readability and maintainability.

    • Java ensures handling of every error.

The Exception Class
  • All exceptions inherit from java.lang.Exception. In Java, all exception classes are subclasses of the java.lang.Exception class, which is the root of the exception hierarchy. This inheritance ensures that all exceptions have a common set of properties and methods.

  • Important methods:

    • printStackTrace(): Prints the method call sequence leading to the exception. This method is useful for debugging as it provides a stack trace that shows the sequence of method calls that led to the exception, allowing developers to identify the source of the error.

    • getMessage() and getLocalizedMessage(): Returns a detailed error message. These methods return a string that provides additional information about the exception, such as a description of the error that occurred. The getLocalizedMessage() method returns a message that is localized for the user's locale, if available.

    • toString(): Returns the Exception class name and a detailed message. This method returns a string representation of the exception object, which includes the name of the exception class and a detailed error message.

  • The Exception class is derived from the Throwable class, inheriting printStackTrace() and getMessage(). The java.lang.Exception class is a subclass of the java.lang.Throwable class, from which it inherits methods like printStackTrace() and getMessage(). The Throwable class is the superclass of all errors and exceptions in Java.

Exception Class Hierarchy
  • Exception

    • RuntimeException

      • ArithmeticException

      • NumberFormatException

      • ArrayIndexOutOfBoundsException

      • … other RuntimeExceptions

    • IOException

    • NoSuchMethodException

    • ClassNotFoundException

    • ClassCastException

    • NullPointerException

    • IllegalArgumentException

    • IndexOutOfBoundsException

Exception Handling: Mandatory in Java
  • Other languages might allow ignoring exceptions, leading to sloppy programs. In some programming languages, it is possible to ignore exceptions or errors, which can lead to poorly written programs that are difficult to debug and maintain.

  • Java forces exception handling for better program quality. Java requires developers to either handle exceptions using try-catch blocks or declare that the method throws the exception using the throws clause. This forces developers to consider potential error conditions and handle them appropriately, leading to more robust and reliable code.

Dealing with Exceptions
  • Throwing an exception: Creating an object that represents an error. When an error condition is detected, an exception object is created to encapsulate information about the error, such as the type of error, a descriptive message, and the state of the program at the time the error occurred.

  • Handling an exception: Detecting and addressing the exception. Handling an exception involves detecting the error and taking appropriate action to resolve it or mitigate its impact on the program's execution.

  • Java throws an exception when an error occurs, creating an exception object. When an error occurs during program execution, Java automatically creates an exception object and throws it, signaling that an exceptional condition has arisen.

  • The program can catch the object and attempt recovery, or ignore it, leading to a crash. When an exception is thrown, the program can either catch the exception and attempt to recover from the error, or ignore it, which will typically result in the program terminating with an error message.

Common Exception Types
  • NullPointerException: Attempting to use a null object. This exception occurs when the program tries to access the methods or properties of an object that has a null reference, meaning that it does not point to any actual object in memory.

  • ClassCastException: Incorrect type casting. This exception is thrown when the program attempts to cast an object to a class of which it is not an instance or a subclass.

  • ArithmeticException: Arithmetic error (e.g., division by zero). This exception occurs when an arithmetic operation, such as division, results in an error condition, such as dividing a number by zero.

  • NumberFormatException: Incorrect number format. This exception is thrown when the program tries to convert a string to a numeric type, but the string does not have the appropriate format (e.g., attempting to convert "abc" to an integer).

  • IOException: Input/output problem. This exception is a general exception that occurs when there is a problem with input or output operations, such as reading from or writing to a file.

  • FileNotFoundException: File not found. This exception is thrown when the program attempts to open or access a file that does not exist in the specified location.

  • EOFException: Attempt to read past the end of a file. This exception occurs when the program tries to read data from a file, but the end of the file has been reached.

  • There exist over 200 pre-defined exceptions. In Java, there are more than 200 predefined exception classes that cover a wide range of error conditions that may occur during program execution. These exceptions are organized into a hierarchy, with the java.lang.Exception class as the root.

Try-Catch Control Structure
  • Java's control structure for exception handling: Try-Catch. The try-catch block is a fundamental control structure in Java for handling exceptions. It allows developers to enclose a block of code that may potentially throw an exception within a try block and provide one or more catch blocks to handle specific types of exceptions that may occur.

  • Separates normal code from error-handling code. The try-catch block separates the normal flow of execution from the error-handling logic, making the code more organized and easier to understand. The try block contains the code that is likely to throw an exception, while the catch blocks contain the code that handles the exception if it occurs.

  • Syntax:

    try {
        // statements to try that may cause an exception
    } catch (Exception ex) {
        // statements to execute when an exception occurs
    }
    
Complete Try-Catch Syntax

String[] students = {"Bob", "Steve", "Larry"};

try {
    // statements to try that may cause an exception
} [
    catch (MostSpecificExceptionType exceptionName) {
        // statements to execute when an exception occurs
    }
] [
    catch (NextSpecificExceptionType exceptionName) {
        // statements to execute when an exception occurs
    }
] [
    catch (LeastSpecificExceptionType exceptionName) {
        // statements to execute when an exception occurs
    }
] [
    finally {
        // statements that always execute after the try-catch,
        // regardless of whether or not an exception occurred.
    }
]
How Try-Catch Works
  • Code in the Try block executes. The code inside the try block is executed in a protected region. If an exception occurs during the execution of this code, the control is transferred to the appropriate catch block.

  • Catch phrases are skipped if no exceptions occur. If the code inside the try block executes without throwing any exceptions, the catch blocks are skipped, and the program continues with the code after the try-catch block.

  • If an exception arises, the program jumps to the first applicable Catch clause. When an exception is thrown within the try block, the program searches for the first catch block that can handle the exception. The catch block must specify an exception type that is the same as or a superclass of the exception type thrown.

  • Finally code executes regardless of exceptions. The finally block is an optional block that contains code that is always executed, regardless of whether an exception was thrown or caught. This is often used to release resources or perform cleanup operations.

The Finally Block
  • The Finally block always executes in the Try-Catch-Finally structure. The finally block is guaranteed to be executed regardless of whether an exception is thrown or caught. It is typically used to release resources, such as closing files or releasing database connections, to prevent resource leaks.

  • If no exception occurs, it executes after the try code. If the code in the try block executes without throwing any exceptions, the finally block is executed after the try block.

  • If an exception occurs, it executes after the appropriate catch code. If an exception is thrown and caught by a catch block, the finally block is executed after the catch block.

  • It executes even with a return statement in try or catch blocks, but before the return is executed. Even if the try or catch blocks contain a return statement, the finally block is executed before the method returns. This ensures that resources are released or cleanup operations are performed before the method exits.

  • A return statement cannot be included in a finally block. It is generally not recommended to include a return statement in a finally block because it can override the return value of the try or catch blocks, leading to unexpected behavior.

Try-Catch Example
String input;
double rate;
double amount;
try {
    input = JOptionPane.showInputDialog("Enter the rate");
    rate = Double.parseDouble(input);
    input = JOptionPane.showInputDialog("Enter the amount");
    amount = Double.parseDouble(input);
    amount = amount * (1 + rate);
} catch (Exception ex) {
    JOptionPane.showMessageDialog(null, "An error occurred: " + ex.getMessage());
    rate = 0;
    amount = 0;
}
JOptionPane.showMessageDialog(null, "The total is $" + amount);
  • Issue: The finally block execution means that rate and amount are reset to zero even when no problems are encountered, so the output will display $0.00.

Ordering Catch Statements
  • Multiple Catch statements can follow a Try. A try block can be followed by multiple catch blocks, each of which handles a different type of exception. This allows the program to handle different error conditions separately and take appropriate action for each.

  • The first Catch that can handle the exception catches it. When an exception is thrown, the program searches for the first catch block that can handle the exception. The catch block must specify an exception type that is the same as or a superclass of the exception type thrown.

  • Order catch statements from most specific to most general. When multiple catch blocks are present, they should be ordered from most specific to most general. This ensures that the most specific exception is caught by the appropriate catch block, rather than being caught by a more general catch block.

  • Example:

    try {
        // try statement block
    } catch (EOFException ex) {
        // catch statement block
    } catch (FileNotFoundException ex) {
        // catch statement block
    } catch (IOException ex) {
        // catch statement block
    } catch (Exception ex) {
        // catch statement block
    }
    
Checked vs. Unchecked Exceptions
  • Checked exceptions: Compiler-recognized exceptions (e.g., FileNotFoundException, EndOfFileException, Exception, excluding RuntimeException). Checked exceptions are exceptions that the compiler requires to be either caught or declared as thrown by the method. If a method throws a checked exception, it must be declared in the method's throws clause.

    • Methods throwing checked exceptions must declare it using the throws statement. When a method throws a checked exception, it must declare the exception in its throws clause. This informs the caller of the method that the method may throw the exception, and the caller must either catch the exception or declare that it throws the exception as well.

  • Unchecked exceptions: Compiler cannot determine at compile time (e.g., RuntimeException, potential divide-by-zero). Unchecked exceptions are exceptions that the compiler does not require to be caught or declared as thrown by the method. These exceptions typically indicate programming errors, such as null pointer exceptions or array index out of bounds exceptions.

    • The compiler doesnt check unchecked exceptions, so the throws statement isnt required. Because the compiler does not check unchecked exceptions, methods are not required to declare that they throw these exceptions in their throws clauses.

Throwing Exceptions
  • The throw statement is used to create exceptions and mark methods that may throw checked exceptions. The throw statement is used to explicitly throw an exception in Java. This allows developers to signal that an error condition has occurred and transfer control to the appropriate exception handler.

  • When to throw:

    • Improper use of a class or its methods. If a class or its methods are used in an incorrect or inappropriate way, an exception may be thrown to indicate that the class is being misused.

    • Method unable to complete its task. If a method is unable to complete its task due to an error