Learn Java in 10 DaysDay 8: Exception Handling and File I/O

Day 8: Exception Handling and File I/O

What You'll Learn Today

  • Exception basics (try-catch-finally)
  • Types of exceptions (checked vs unchecked)
  • Custom exceptions
  • Reading and writing files
  • try-with-resources

What Are Exceptions?

An exception is an error that occurs during program execution. If not handled properly, it will cause the program to crash.

// Examples of exceptions
int[] nums = {1, 2, 3};
System.out.println(nums[5]); // ArrayIndexOutOfBoundsException

String s = null;
s.length(); // NullPointerException

int result = 10 / 0; // ArithmeticException

try-catch-finally

try {
    int result = 10 / 0;
    System.out.println(result);
} catch (ArithmeticException e) {
    System.out.println("Error: " + e.getMessage());
} finally {
    System.out.println("This always executes");
}
flowchart TB
    Try["try block<br>Code that might throw"]
    Catch["catch block<br>Handle the exception"]
    Finally["finally block<br>Always executes"]
    Success["Normal completion"]
    Try -->|"Exception thrown"| Catch --> Finally
    Try -->|"No exception"| Success --> Finally
    style Try fill:#3b82f6,color:#fff
    style Catch fill:#ef4444,color:#fff
    style Finally fill:#22c55e,color:#fff

Catching Multiple Exceptions

try {
    String input = "abc";
    int number = Integer.parseInt(input);
    int result = 100 / number;
} catch (NumberFormatException e) {
    System.out.println("Cannot convert to number: " + e.getMessage());
} catch (ArithmeticException e) {
    System.out.println("Arithmetic error: " + e.getMessage());
} catch (Exception e) {
    System.out.println("Unexpected error: " + e.getMessage());
}

// Multi-catch (Java 7+)
try {
    // ...
} catch (NumberFormatException | ArithmeticException e) {
    System.out.println("Input or arithmetic error: " + e.getMessage());
}

Key point: Order catch blocks from most specific to most general. Exception (the parent of all) should come last.


Exception Hierarchy

flowchart TB
    Throwable["Throwable"]
    Error["Error<br>(unrecoverable)"]
    Exception["Exception"]
    RuntimeException["RuntimeException<br>(unchecked)"]
    IOException["IOException<br>(checked)"]
    NPE["NullPointerException"]
    IAE["IllegalArgumentException"]
    AIOOBE["ArrayIndexOutOfBoundsException"]
    Throwable --> Error
    Throwable --> Exception
    Exception --> RuntimeException
    Exception --> IOException
    RuntimeException --> NPE
    RuntimeException --> IAE
    RuntimeException --> AIOOBE
    style Error fill:#ef4444,color:#fff
    style RuntimeException fill:#f59e0b,color:#fff
    style IOException fill:#3b82f6,color:#fff
Type Examples Must catch?
Checked exception IOException, SQLException Yes
Unchecked exception NullPointerException, IllegalArgumentException No
Error OutOfMemoryError, StackOverflowError No (usually not caught)

throws and throw

throws (declaring that a method may throw an exception)

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

// Propagate checked exception to the caller
String readFile(String path) throws IOException {
    return Files.readString(Path.of(path));
}

throw (explicitly throwing an exception)

void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age must be non-negative: " + age);
    }
    this.age = age;
}

Custom Exceptions

// Checked exception
class InsufficientFundsException extends Exception {
    private final double amount;

    InsufficientFundsException(double amount) {
        super("Insufficient funds: $" + amount + " needed");
        this.amount = amount;
    }

    double getAmount() { return amount; }
}

// Usage
class BankAccount {
    private double balance;

    BankAccount(double balance) { this.balance = balance; }

    void withdraw(double amount) throws InsufficientFundsException {
        if (amount > balance) {
            throw new InsufficientFundsException(amount - balance);
        }
        balance -= amount;
    }
}

try-with-resources

Automatically closes resources that implement AutoCloseable.

import java.io.*;

// The old way
BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (reader != null) {
        try { reader.close(); } catch (IOException e) { }
    }
}

// try-with-resources (Java 7+)
try (var reader = new BufferedReader(new FileReader("data.txt"))) {
    String line = reader.readLine();
    System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
}
// reader is automatically closed

Best practice: Always use try-with-resources for file operations and database connections.


Reading and Writing Files

The Files Class (Recommended)

import java.nio.file.Files;
import java.nio.file.Path;
import java.io.IOException;
import java.util.List;

// Read entire file as a string
String content = Files.readString(Path.of("data.txt"));

// Read line by line
List<String> lines = Files.readAllLines(Path.of("data.txt"));
for (String line : lines) {
    System.out.println(line);
}

// Write a string
Files.writeString(Path.of("output.txt"), "Hello, Java!");

// Write lines
List<String> data = List.of("Line 1", "Line 2", "Line 3");
Files.write(Path.of("output.txt"), data);

Checking File Information

Path path = Path.of("data.txt");

Files.exists(path)          // does the file exist?
Files.isRegularFile(path)   // is it a regular file?
Files.isDirectory(path)     // is it a directory?
Files.size(path)            // size in bytes

BufferedReader / BufferedWriter

Best suited for processing large files.

// Reading
try (var reader = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
}

// Writing
try (var writer = Files.newBufferedWriter(Path.of("output.txt"))) {
    writer.write("Hello");
    writer.newLine();
    writer.write("World");
}

Directory Operations

// Create directories
Files.createDirectories(Path.of("path/to/dir"));

// List files
try (var stream = Files.list(Path.of("."))) {
    stream.forEach(System.out::println);
}

// Copy, move, delete
Files.copy(Path.of("src.txt"), Path.of("dest.txt"));
Files.move(Path.of("old.txt"), Path.of("new.txt"));
Files.delete(Path.of("temp.txt"));

Reading Files with Scanner

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

try (var scanner = new Scanner(new File("data.txt"))) {
    while (scanner.hasNextLine()) {
        String line = scanner.nextLine();
        System.out.println(line);
    }
} catch (FileNotFoundException e) {
    System.out.println("File not found: " + e.getMessage());
}

Hands-On: CSV File Processor

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

record Student(String name, int score) {
    String grade() {
        if (score >= 90) return "A";
        if (score >= 80) return "B";
        if (score >= 70) return "C";
        return "D";
    }

    @Override
    public String toString() {
        return String.format("%s: %d points (%s)", name, score, grade());
    }
}

public class CsvProcessor {
    static List<Student> readCsv(Path path) throws IOException {
        List<Student> students = new ArrayList<>();
        List<String> lines = Files.readAllLines(path);

        for (int i = 1; i < lines.size(); i++) { // skip header
            String[] parts = lines.get(i).split(",");
            String name = parts[0].trim();
            int score = Integer.parseInt(parts[1].trim());
            students.add(new Student(name, score));
        }
        return students;
    }

    static void writeSummary(Path path, List<Student> students) throws IOException {
        var sb = new StringBuilder();
        sb.append("=== Grade Report ===%n".formatted());

        double avg = students.stream()
            .mapToInt(Student::score)
            .average()
            .orElse(0);

        for (Student s : students) {
            sb.append(s).append(System.lineSeparator());
        }
        sb.append("---%n".formatted());
        sb.append("Average: %.1f%n".formatted(avg));

        Files.writeString(path, sb.toString());
        System.out.println("Report written to: " + path);
    }

    public static void main(String[] args) {
        try {
            String csv = """
                    name,score
                    Alice,85
                    Bob,92
                    Charlie,78
                    Diana,95
                    Evan,67
                    """;
            Path csvPath = Path.of("students.csv");
            Files.writeString(csvPath, csv);

            List<Student> students = readCsv(csvPath);
            students.forEach(System.out::println);

            writeSummary(Path.of("report.txt"), students);
        } catch (IOException e) {
            System.err.println("File operation error: " + e.getMessage());
        }
    }
}

Summary

Concept Description
try-catch Catch and handle exceptions
finally Block that always executes
throws Declare that a method may throw
throw Explicitly throw an exception
Checked exception Must be caught or declared with throws
Unchecked exception Subclass of RuntimeException
try-with-resources Automatically close resources
Files Utility class for file operations
Path Represents a file path

Key Takeaways

  1. Manage resources with try-with-resources
  2. Handle checked exceptions with catch or throws
  3. Use the Files class for file operations
  4. Use custom exceptions to represent domain-specific errors

Exercises

Exercise 1: Basic

Write a program that takes numeric input from the user and displays an appropriate error message when a division by zero is attempted.

Exercise 2: Applied

Create a program that reads a text file and displays the line count, word count, and character count (a simplified wc command).

Challenge

Implement a simple login system using custom exceptions. Define UserNotFoundException and InvalidPasswordException, and validate username and password inputs.


References


Next up: In Day 9, you'll learn about Collections and Generics -- mastering List, Map, Set, and the Stream API.