Java 7: Files.readAllLines and Files.write
Prior to Java 7, reading from and writing to files required a significant amount of boilerplate code. Developers had to manually instantiate FileReader, wrap it in a BufferedReader, iterate through the file line by line using a while loop, and carefully manage exceptions and resource closures using finally blocks. Writing data was equally cumbersome, involving FileWriter and BufferedWriter. This verbosity made simple file I/O operations tedious and prone to resource leak errors.
The introduction of the NIO.2 API in Java 7 revolutionized file handling with the java.nio.file.Files utility class. This class provides static methods that perform common file operations in a single line of code. Two of the most useful methods for text manipulation are Files.readAllLines() and Files.write(). These methods abstract away the underlying streams, readers, and writers, allowing developers to focus directly on the data.
Files.readAllLines(Path path, Charset cs) reads an entire file into memory and returns a List<String>, where each element represents a line in the file. This is incredibly convenient for processing small to medium-sized configuration files or logs. Conversely, Files.write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options) takes a collection of strings and writes them directly to a file, handling file creation or overwriting automatically based on the provided options.
While these methods are powerful and clean, developers must exercise caution when dealing with very large files. Because readAllLines() loads the entire file contents into the application's heap memory at once, it can easily cause an OutOfMemoryError if the file is larger than the available RAM. For massive files, the traditional streaming approach or the newer Files.lines() (introduced in Java 8) is still required.
How it Works
You use the Paths.get() method to define a Path object representing your file. Then, you simply pass this path to Files.readAllLines() to get a List of strings. To write, you pass the path and an Iterable (like a List) of strings to Files.write(). The methods automatically handle opening and closing the file resources.
Java Example
Scenario 1: Writing and Reading a Simple Text File
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.io.IOException;
public class FilesUtilityExample1 {
public static void main(String[] args) {
Path filePath = Paths.get("sample_log.txt");
List<String> linesToWrite = Arrays.asList("Line 1: Application started", "Line 2: Processing data", "Line 3: Application closed");
try {
// Write lines to file
Files.write(filePath, linesToWrite, StandardCharsets.UTF_8);
System.out.println("File written successfully.");
// Read lines from file
List<String> readLines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
System.out.println("Reading from file:");
for (String line : readLines) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output
File written successfully.
Reading from file:
Line 1: Application started
Line 2: Processing data
Line 3: Application closed
Scenario 2: Appending Data to an Existing File
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.io.IOException;
public class FilesUtilityExample2 {
public static void main(String[] args) {
Path filePath = Paths.get("sample_log.txt");
List<String> moreLines = Arrays.asList("Line 4: System rebooted");
try {
// Append line using StandardOpenOption.APPEND
Files.write(filePath, moreLines, StandardCharsets.UTF_8, StandardOpenOption.APPEND);
System.out.println("Data appended successfully.");
// Read to verify
List<String> allLines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
System.out.println("Updated File Content:");
System.out.println(String.join("\n", allLines));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output
Data appended successfully.
Updated File Content:
Line 1: Application started
Line 2: Processing data
Line 3: Application closed
Line 4: System rebooted
Scenario 3: Handling Missing Files
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
public class FilesUtilityExample3 {
public static void main(String[] args) {
Path missingFile = Paths.get("does_not_exist.txt");
try {
Files.readAllLines(missingFile, StandardCharsets.UTF_8);
} catch (NoSuchFileException e) {
System.out.println("Error: The specified file could not be found -> " + e.getFile());
} catch (IOException e) {
System.out.println("General I/O Error.");
}
}
}
Output
Error: The specified file could not be found -> does_not_exist.txt
Key Points
- Provides a one-liner approach for reading and writing entire text files.
- Eliminates boilerplate code for streams and resource management.
- Returns a convenient
List<String>for reading and accepts anIterable<String>for writing. - Not suitable for extremely large files as it loads all content into memory simultaneously.
Comments