What you'll learn
Programs need to save and load data — settings, logs, reports, user files. This module covers Java's input/output (I/O): reading and writing files the modern, easy way with NIO.2, plus the stream classes underneath.
By the end you'll be able to:
- Tell apart byte and character streams
- Work with files using the modern
PathandFilesAPI - Read and write text and binary files
- Use buffered streams for performance
- Traverse directories and understand serialization
Byte vs character streams
Java models I/O as streams of data flowing in or out. There are two families, and picking the right one matters:
Byte streams
Raw 8-bit data — images, audio, PDFs, anything binary.
InputStream · OutputStream
Character streams
Text, decoded with a charset (UTF-8) into readable characters.
Reader · Writer
Note
Reader/Writer); everything else → byte streams (InputStream/OutputStream). For text, always be explicit about the charset (UTF-8) to avoid garbled characters across systems.NIO.2: Path & Files
Modern Java (since Java 7) offers NIO.2 — the java.nio.file package — which is far cleaner than the old File class. A Path represents a file or folder location, and the Files utility class does the actual work with simple static methods. This is what you should reach for today.
Reading & writing text
With NIO.2, reading and writing a whole text file is a single line. Files.writeString and Files.readString handle opening, closing, and encoding for you:
import java.nio.file.*;
import java.util.List;
Path file = Path.of("notes.txt");
Files.writeString(file, "Line 1\nLine 2\n"); // write (creates/overwrites)
String content = Files.readString(file); // read the whole file
System.out.print(content);
List<String> lines = Files.readAllLines(file); // read as a list of lines
System.out.println("Lines: " + lines.size());By default writing overwrites. To add to an existing file, pass the APPEND option:
import java.nio.file.*;
Path file = Path.of("log.txt");
Files.writeString(file, "start\n"); // fresh
Files.writeString(file, "more\n", StandardOpenOption.APPEND); // append
System.out.println(Files.exists(file)); // true
System.out.print(Files.readString(file));| Class / method | Use for |
|---|---|
Files.readString / writeString | read or write an entire text file |
Files.readAllLines / write | text as a List of lines |
Files.readAllBytes / write | binary data |
Files.exists / delete / copy | file operations |
Path.of(...) | build a path |
Buffered streams
For large files or line-by-line processing, use buffered streams. They read and write in big chunks instead of one character at a time, which is dramatically faster. Wrap a reader/writer in a Buffered version, and use try-with-resources so it always closes:
import java.io.*;
// Buffered wrappers read in big chunks — far faster for large files.
// try-with-resources closes the reader automatically.
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Could not read the file");
}Tip
Files.lines(path) returns a stream of lines you can filter and process lazily — combine it with everything you learned in Module 18.Directories & file attributes
Files also handles folders: createDirectories, list (one level), and walk (recursive). Because these return streams, close them with try-with-resources:
import java.nio.file.*;
import java.util.stream.Stream;
// list only the regular files in the current directory
try (Stream<Path> files = Files.list(Path.of("."))) {
files.filter(Files::isRegularFile)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}Serialization
Serialization converts an object into a byte stream (to save or send it), and deserialization rebuilds it. A class opts in by implementing the marker interface Serializable, then ObjectOutputStream/ObjectInputStream do the work.
Handle with care
Recap & quick check
Key takeaways
- Byte streams (InputStream/OutputStream) for binary; character streams (Reader/Writer) for text.
- Prefer the modern NIO.2 API: Path for locations, Files for operations.
- Files.readString/writeString read or write a whole text file in one line; APPEND to add on.
- Use buffered streams (or Files.lines) for large files and speed.
- Serialization needs Serializable but is brittle and risky for untrusted data — prefer JSON.
Quick check
1. Which stream family should you use for reading an image file?
2. What is the modern way to read a whole text file?
3. Why use buffered streams?
4. What interface must a class implement to be serialized?
5. For very large files, how do you avoid loading everything into memory?
Great — your programs can now persist and load data. Next up: Module 20 — Regular Expressions.