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
catchblocks 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
- Manage resources with try-with-resources
- Handle checked exceptions with
catchorthrows - Use the
Filesclass for file operations - 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.