File I/O and NIO
7 pages · ~5 min✓ Reviewed
Builds on Collections Framework. Next up: Lambdas and Streams.
Java I/O Mental Model
1. Bytes vs Characters
| Byte streams | Character streams | |
|---|---|---|
| Classes | InputStream / OutputStream | Reader / Writer |
| Unit | raw 8-bit bytes | 16-bit chars |
| Charset | none | decodes / encodes via a charset |
| Use for | images, PDFs, zips, any binary | text |
- Bridge classes
InputStreamReader/OutputStreamWriterturn a byte stream into a character stream - Always give the bridge an explicit
Charset
2. Decorator Layers
- Decorator pattern: each wrapper adds one job around the stream inside it
- FileInputStreamraw bytes
- InputStreamReaderdecode with UTF_8
- BufferedReaderchunks and lines
java
var br = new BufferedReader(
new InputStreamReader(
new FileInputStream(f), UTF_8));bytes, then decode, then buffer
3. Common Mistake
Reader on binary data
A Reader on an image or zip corrupts it: decoding replaces invalid bytes. Use InputStream for binary.
try-with-resources and BufferedReader
1. try-with-resources
try (var r = new BufferedReader(...)) { }closes any AutoCloseable- Closes even when an exception is thrown
- Closing the outer wrapper closes the inner stream
- Close order is reverse of declaration
- A failing
close()is suppressed: read it withgetSuppressed() - The primary exception is not lost
- Open in
- Open br
- Body
- Close br
- Close in
2. Reading lines
readLine()returns null at end of file
Path and Files Essentials
1. Path: a location object
- Path is an immutable location:
Path.of("a","b.txt")orPaths.get(...) - It may not exist on disk; creating one touches nothing
- Operations are pure string logic, no disk access
| Method | Does |
|---|---|
resolve(other) | appends other to this path |
relativize(other) | computes the relative path between two |
normalize() | removes . and .. |
toAbsolutePath() |
Reading and Writing Files
1. Reading: Small vs Large
| Call | Returns | Memory |
|---|---|---|
readString(p) | String | all loaded |
readAllLines(p) | List<String> | all loaded |
readAllBytes(p) | byte[] | all loaded |
Files.lines(p) | Stream<String> | lazy |
Character Encodings and Directory Walking
1. Charsets and defaults
| Charset | Bytes per char |
|---|---|
| UTF-8 | 1 to 4 |
| ISO-8859-1 | 1 |
| UTF-16 | 2 or 4 |
new FileReader(f): platform default before Java 18- Java 18+ (JEP 400): default is UTF-8
- Still pass
StandardCharsets.UTF_8explicitly
| API |
|---|
Java File I/O Cheat Sheet
1. Streams and Closing
| Data | Classes | Charset |
|---|---|---|
| Binary | InputStream / OutputStream | none |
| Text | Reader / Writer | explicit UTF_8 |
- Always try-with-resources
- Resources close in reverse order of declaration
close()errors become suppressed:getSuppressed()
2. Read and Write
Check yourself
try (var a = new Res("A"); var b = new Res("B")) { throw new IllegalStateException(); }. Both close() methods throw IOException. What reaches the caller, and which closes first?
IllegalStateExceptionarrives, the body's exception stays primary- The two IOExceptions are attached, read with
getSuppressed() - B closes first, then A (reverse order)
p does not exist yet. What does Files.writeString(p, "hi", StandardOpenOption.APPEND) do?
- Throws
NoSuchFileException - Passing any option replaces the defaults, so CREATE is gone
- Fix: pass
CREATE, APPEND