File Handling
Almost every real program reads from or writes to files — configuration, logs, data export, report generation. Java provides several layers of file IO from low-level streams to high-level convenience classes.
The Java IO Hierarchy
Java's file IO is built from composable classes. Understanding the layer is important:
- Byte streams (
FileInputStream,FileOutputStream) — read/write raw bytes. Used for binary files (images, PDFs). - Character streams (
FileReader,FileWriter) — handle character encoding automatically. Used for text files. - Buffered streams (
BufferedReader,BufferedWriter) — wrap character streams for efficient line-at-a-time IO. Most commonly used for text file processing. - NIO.2 (
java.nio.file) — modern API (Java 7+).FilesandPathprovide concise, safe operations.
Writing Files — FileWriter + BufferedWriter
import java.io.*;
// Writing with try-with-resources (auto-closes on exit or exception)
String filename = "attendance.txt";
try (FileWriter fw = new FileWriter(filename);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write("Your IT Tutor Attendance Register");
bw.newLine();
bw.write("=".repeat(30));
bw.newLine();
bw.write("Monday: 28 learners present");
bw.newLine();
bw.write("Tuesday: 31 learners present");
bw.newLine();
System.out.println("File written successfully.");
} catch (IOException e) {
System.out.println("Write failed: " + e.getMessage());
}
// Appending to an existing file (second param true = append mode)
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename, true))) {
bw.write("Wednesday: 27 learners present");
bw.newLine();
} catch (IOException e) {
e.printStackTrace();
}
Reading Files — BufferedReader
try (BufferedReader reader = new BufferedReader(new FileReader("attendance.txt"))) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
lineNumber++;
System.out.printf("%3d: %s%n", lineNumber, line);
}
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("Read error: " + e.getMessage());
}
Modern NIO.2 API (Recommended)
The java.nio.file package (Java 7+) provides a cleaner, more concise API for common file operations. Use it for new code.
import java.nio.file.*;
import java.util.List;
Path filePath = Path.of("learners.txt");
// Write all lines at once
List<String> lines = List.of(
"Thandi Mokoena - Java - 74.5%",
"Sipho Dlamini - Python - 81.0%",
"Lerato Khumalo - SQL - 68.0%"
);
Files.write(filePath, lines); // overwrites
// Append
Files.writeString(filePath, "Bongani Nkosi - C# - 88.5%\n",
StandardOpenOption.APPEND);
// Read all lines
List<String> read = Files.readAllLines(filePath);
for (String line : read) System.out.println(line);
// Read as a single string
String content = Files.readString(filePath);
System.out.println("Total chars: " + content.length());
// Check existence
System.out.println("Exists: " + Files.exists(filePath));
System.out.println("Size: " + Files.size(filePath) + " bytes");
// Delete
// Files.delete(filePath); // throws if not found
Files.deleteIfExists(filePath); // safe
Reading CSV Data
CSV (Comma-Separated Values) is the most common data interchange format. Parsing it with Java IO is a fundamental skill.
// Assuming marks.csv:
// name,course,mark
// Thandi,Java,74.5
// Sipho,Python,81.0
ArrayList<Learner> fromFile = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader("marks.csv"))) {
String line;
reader.readLine(); // skip header line
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",");
if (parts.length == 3) {
String name = parts[0].trim();
String course = parts[1].trim();
double mark = Double.parseDouble(parts[2].trim());
fromFile.add(new Learner(name, 0, course, mark));
}
}
} catch (IOException | NumberFormatException e) {
System.out.println("Error reading CSV: " + e.getMessage());
}
System.out.println("Loaded " + fromFile.size() + " learners from file.");
fromFile.forEach(l -> System.out.println(" " + l));
Practice Task
Your Turn
Write a LearnerRegistry program: (1) write a method saveToFile(ArrayList<Learner> learners, String filename) that writes each learner as a CSV line (name,course,mark), (2) write a method loadFromFile(String filename) that reads the file back and returns an ArrayList<Learner>, (3) in main: create 5 learners, save to registry.csv, clear the list, reload from file, and print each loaded learner. Handle all possible exceptions with meaningful messages.
Common Mistakes
- Not closing resources — without try-with-resources, an exception before
close()leaves files open. Always use try-with-resources. - Assuming the file exists — always catch
FileNotFoundExceptionseparately and provide a clear message. - Writing without flushing —
BufferedWriterbuffers output. It is flushed when closed. Do not rely on the buffer being written without closing. - Platform line endings — use
System.lineSeparator()orbw.newLine()instead of\nfor cross-platform compatibility. - Not handling
NumberFormatExceptionwhen parsing numeric fields from a text file — user-generated data is rarely perfectly formatted.
Professional Tip
File paths are relative to the working directory — which is usually the project root when run from an IDE, and the directory where you run java from the terminal. Use absolute paths or System.getProperty("user.dir") to understand where files are being written.
Mini Quiz
What does try-with-resources guarantee in Java IO?