Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Java Exception Handling and I/O Stream Essentials

Notes 1

Exception Handling

What Are Exceptions?

An exception is an abnormal event that disrupts a program's normal execution flow. When an error occurs, the JVM or application code creates a Throwable subclass object and throws it. The runtime system then searches the call stack for an appropriate exception handler (a catch block) to process the event.

Three Exception Types

  1. Checked Exceptions: Represent recoverable error conditions (e.g., IOException). Methods must either catch these exceptions or declare them in their signature using the throws clause.
  2. Errors: System-level failures (e.g., OutOfMemoryError, NoClassDefFoundError). These are not required to be handled, as recovery is typically impossible.
  3. Runtime Exceptions: Indicate programming errors (e.g., NullPointerException, IndexOutOfBoundsException). These are unchecked, meaning methods do not need to catch or declare them.

Handling Exceptions

Use try, catch, and finally blocks to manage exceptions:

  • Try Block: Encloses code that may throw exceptions.
  • Catch Blocks: Handle specific exception types. Multiple catch blocks must be ordered from most specific to least specific. Java 7+ allows a single catch block to handle multiple exception types using the | operator.
  • Finally Block: Runs regardless of whether an exception is thrown (unless the JVM exits via System.exit()). Ideal for cleanup tasks like closing streams.

Example: Try-Catch-Finally

public void writeList() {
    PrintWriter out = null;
    try {
        System.out.println("Entering try statement");
        out = new PrintWriter(new FileWriter("OutFile.txt"));
        for (int i = 0; i < 10; i++) {
            out.println("Value at: " + i + " = " + list.get(i));
        }
    } catch (IndexOutOfBoundsException e) {
        System.err.println("Caught IndexOutOfBoundsException: " + e.getMessage());
    } catch (IOException e) {
        System.err.println("Caught IOException: " + e.getMessage());
    } finally {
        if (out != null) {
            System.out.println("Closing PrintWriter");
            out.close();
        } else {
            System.out.println("PrintWriter not open");
        }
    }
}

Try-With-Resources

Introduced in Java 7, this statement automatically closes resources that implement the AutoCloseable interface (e.g., streams, JDBC connections). Resources are declared in the try parentheses and closed in reverse order of declaration. Exceptions from closing are suppressed and can be retrieved via getSuppressed().

static String readFirstLineFromFile(String path) throws IOException {
    try (FileReader fr = new FileReader(path);
         BufferedReader br = new BufferedReader(fr)) {
        return br.readLine();
    }
}

Throwing Exceptions

Use the throw statement to explicitly throw an exception:

public Object pop() {
    if (size == 0) {
        throw new EmptyStackException();
    }
    Object obj = objectAt(size - 1);
    setObjectAt(size - 1, null);
    size--;
    return obj;
}

Methods that throw checked exceptions must declare them in their signature:

public void writeList() throws IOException {
    // Code that may throw IOException
}

Chained Exceptions

Track the root cause of an exception using chained exceptions. Use initCause() to set the root cause and getCause() to retrieve it:

try {
    // Code that throws IOException
} catch (IOException e) {
    SampleException se = new SampleException("Failed to process file");
    se.initCause(e);
    throw se;
}

Unchecked Exceptions: A Debate

Avoid using unchecked exceptions to bypass the catch/declare requirement. Checked exceptions are part of a method's API contract, helping clients understand error conditions. Use unchecked exceptions only for programming errors that clients cannot recover from.

Advantages of Exceptions

  1. Separate Error Handling: Isolates error logic from normal code, improving readability.
  2. Propagate Errors Cleanly: Automatically propagates exceptions up the call stack without manual error code passing.
  3. Error Grouping: Uses class hierarcheis to group related exceptions, enabling general or specific handling.

I/O Streams

I/O streams represent input sources or output destinations (e.g., files, network sockets, memory arrays). They process data sequentially, one element at a time.

Byte Streams

Byte streams (subclasses of InputStream and OutputStream) handle raw 8-bit binary data. Use FileInputStream and FileOutputStream for file byte I/O:

public class CopyBytes {
    public static void main(String[] args) throws IOException {
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream("xanadu.txt");
            out = new FileOutputStream("outagain.txt");
            int c;
            while ((c = in.read()) != -1) {
                out.write(c);
            }
        } finally {
            if (in != null) in.close();
            if (out != null) out.close();
        }
    }
}

Byte streams are low-level; prefer character streams for text data, as they handle Unicode and charset conversion.

Character Streams

Character streams (subclasses of Reader and Writer) handle Unicode characters, automatically converting between internal Unicode and the local charset. Use FileReader and FileWriter for basic file character I/O:

public class CopyCharacters {
    public static void main(String[] args) throws IOException {
        FileReader inputStream = null;
        FileWriter outputStream = null;
        try {
            inputStream = new FileReader("xanadu.txt");
            outputStream = new FileWriter("characteroutput.txt");
            int c;
            while ((c = inputStream.read()) != -1) {
                outputStream.write(c);
            }
        } finally {
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        }
    }
}

Character stream are often wrappers over byte streams. Use InputStreamReader and OutputStreamWriter to bridge byte and character streams.

Line-Oriented I/O

For line-based text processing, use BufferedReader (for readLine()) and PrintWriter (for println()):

public class CopyLines {
    public static void main(String[] args) throws IOException {
        BufferedReader inputStream = null;
        PrintWriter outputStream = null;
        try {
            inputStream = new BufferedReader(new FileReader("xanadu.txt"));
            outputStream = new PrintWriter(new FileWriter("characteroutput.txt"));
            String line;
            while ((line = inputStream.readLine()) != null) {
                outputStream.println(line);
            }
        } finally {
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        }
    }
}

readLine() returns a line without the terminator; println() appends the system-specific line terminator.


Questions and Exercises

Exception Handling Questions

  1. Legal Code?

try { } finally { }

   Enswer: Yes. A try block can have a finally block without any catch blocks.

2. **Broad Catch Handler**
   ```java
catch (Exception e) { }

Answer: Catches all Exception subclasses (checked and runtime exceptions). The problem is it's overly broad, potentially hiding unintended exceptions or bugs.

  1. Unreachable Catch Block

try { } catch (Exception e) { } catch (ArithmeticException a) { }

   Answer: Won't compile. The `ArithmeticException` block is unreachable because `Exception` already catches all its subclasses. Catch blocks must be ordered from most specific to least specific.

4. **Scenario Matching**
   - `int[] A; A[0] = 0;` → Runtime Exception (NullPointerException)
   - JVM can't find Java platform classes → Error (NoClassDefFoundError)
   - Reading a stream to EOF → No Exception (read methods return -1 or null)
   - Reading a stream post-EOF → No Exception (read returns -1 again)

### I/O Streams Key Takeaways
- Always close streams to prevent resource leaks (use try-with-resources or finally blocks).
- Use byte streams only for raw binary data; prefer character streams for text.
- Character streams auto-handle charset conversion, making programs more internationalized.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.