Skip to content

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

java
// 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

java
// 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, BufferedWriter

NIO Path & Files

java
// 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

java
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

java
// 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.File is legacy.
Buffered I/O reduces system calls by 10,000x.

Built with VitePress | Software Systems Atlas