Optional: File I/O and Serialization
Read and write files with the Files API, and understand why native Java serialization is legacy material.
Why it matters
Start from the concept, not the syntax
Meet a concrete situation where this idea earns its place, then state the boundary it guards.
Programs that matter read and write data. The stream and reader abstractions handle bytes and text uniformly, the Files API turns common operations into one-liners, and serialization shows why explicit formats usually beat implicit ones.
By the end of this chapter
Leaving streams open or relying on the platform encoding corrupts data and exhausts file handles under load.
- Reader and Writer
- Path and Files
- Serialization trade-offs
Mechanism
How Java expresses the concept
Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.
The mental model
Byte streams (`InputStream`/`OutputStream`) move raw bytes; character streams (`Reader`/`Writer`) move text and handle encoding. Wrap a `Reader` in a `BufferedReader` for efficient line-by-line reading. Always close streams, ideally with try-with-resources.
The `java.nio.file` API centers on `Path` and `Files`: `Files.readString`, `Files.writeString`, `Files.lines` for streaming lines, and `Files.walk` for trees. Prefer these over the legacy `File` methods; they are clearer and throw specific, catchable exceptions.
Serialization turns an object graph into bytes via `ObjectOutputStream` and rebuilds it with `ObjectInputStream`. The class must implement `Serializable` (a marker interface) and ideally declare a `serialVersionUID`. Treat Java native serialization as legacy: it is brittle across versions and a security risk, so prefer JSON or another explicit format for new code.
Text encoding matters. Decode and encode with an explicit `Charset` (UTF-8) instead of relying on the platform default, or the same file can read differently on another machine.
Concept map
Inspect the building blocks as a whole
Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.
Core Java
File I/O and serialization
Files are read through streams and readers. Modern Java adds `Files` and `Path`; older code uses `FileReader` and `BufferedReader`.
- 01Byte vs character streams
- 02BufferedReader lines
- 03Path and Files
- 04Serializable marker
- 05serialVersionUID
- 06Always close streams
Implementation
Encode the smallest runnable example
Write and run the code, then observe the behavior you just predicted with a real Java program.
Runnable snippet path
Predict, run, and change each example
Read all lines
Files.lines streams lines and closes the file automatically via try-with-resources.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Lines {
static long count(Path p) throws IOException {
try (var stream = Files.lines(p)) {
return stream.count();
}
}
}Remember
Pass an explicit Charset when reading text so encoding is not left to the platform.
Change one input, predict the result, then compile or test it before continuing.
Write a serializable object
Implement Serializable and declare a UID; write and read through object streams.
import java.io.*;
class Point implements Serializable {
private static final long serialVersionUID = 1L;
final int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
class Save {
static void save(Point p, String file) throws IOException {
try (var out = new ObjectOutputStream(new FileOutputStream(file))) {
out.writeObject(p);
}
}
}Remember
Native serialization is legacy; prefer JSON for new systems.
Change one input, predict the result, then compile or test it before continuing.
Watch out
Break it on purpose, then repair the right layer
Predict the symptom before running the failure case, and close with the completion checklist.
Deliberate failure
Break
Read a file without closing the stream and without pinning an encoding
Observe
Handles leak over time and the same text decodes differently on another machine
Repair
Close every stream with try-with-resources and pass an explicit UTF-8 charset for all text I/O
Why it matters
Close every stream with try-with-resources and pass an explicit UTF-8 charset for all text I/O
Completion checklist
- Read and write text safely with explicit encoding
- Use Path and Files for file operations
- Explain Serializable and its limits
Go deeper
Official references
The primary sources behind this chapter. Every claim above stays consistent with them.