Metadata Card
- Difficulty: (Novice Village 3-star)
- Prerequisites: Ch1-14 (everything from this volume)
- Core concepts: 8 (File class, byte/character streams, Buffered wrappers, NIO Path/Files, serialization, standard I/O, encoding, try-with-resources)
- Estimated time: 5-7 hrs
The Breakthrough · Tracing the Origins
This is your last lesson in Variable Village. You've mastered variables, control flow, OOP, collections, exceptions, generics, concurrency — you can manipulate data in memory brilliantly. But all your data is alive. When the program closes, it vanishes like a dream.
Memory is temporary. Disk is forever. Every CRUD app you'll ever write — registrations, activity logs, configuration files — relies on I/O.
I/O is the bottleneck: Memory read = 100ns. SSD read = 150,000ns (0.15ms). HDD seek = 10,000,000ns (10ms). I/O is 1,000-100,000x slower than memory.
File Encoding — Always Be Explicit
// WRONG — uses system default encoding
FileReader reader = new FileReader("file.txt");
// CORRECT — explicitly specify charset
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) { ... }
}Streaming — Don't Load Everything
// WRONG — loads entire file into memory
String content = Files.readString(Path.of("huge.log")); // OOM for large files!
// CORRECT — process line by line
try (Stream<String> lines = Files.lines(Path.of("huge.log"), UTF_8)) {
lines.filter(l -> l.contains("ERROR")).forEach(System.out::println);
}I/O Architecture
Byte Streams: InputStream / OutputStream
└── FileInputStream, BufferedInputStream, DataInputStream, ObjectInputStream
Bridge: InputStreamReader (byte → char, with encoding)
Character Streams: Reader / Writer
└── FileReader, BufferedReader, BufferedWriterNIO Path & Files
// Modern I/O (Java 7+)
Files.createDirectories(Path.of("/home/user/logs/2025"));
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Files.writeString(Path.of("output.txt"), "Hello!", UTF_8);Serialization
class Player implements Serializable {
private static final long serialVersionUID = 1L;
private transient String password; // Not serialized
}
// Write
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("player.dat"))) {
oos.writeObject(player);
}
// Read
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("player.dat"))) {
Player p = (Player) ois.readObject();
}Three Levels of File Copy
// Bronze: byte by byte (30s for 100MB)
while ((b = in.read()) != -1) { out.write(b); }
// Silver: buffer array (0.03s for 100MB)
byte[] buffer = new byte[8192];
while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); }
// Gold: Files.copy() (instant)
Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);Final Challenge: Write a multi-format log analyzer. Read server.log (UTF-8, unknown size from 10KB to 2GB). Count log levels (INFO/WARN/ERROR/DEBUG). Stop on FATAL. Output to report.txt. Write errors to error.log.
Traveler's Notes
I/O is the interface between your program and the real world.
Always specify encoding explicitly — FileReader uses system default (wrong for production).
Streaming means processing one chunk at a time — never load entire files.
try-with-resources for all I/O — no more manual close in finally.
NIO Path + Files is the modern way.java.io.Fileis legacy.
Buffered I/O reduces system calls by 10,000x.