Introduction
Reading data from files is a fundamental operation in Java applications, whether for processing configuration files, importing user data, analyzing logs, or loading resources. Java provides multiple classes and approaches for file input, ranging from low-level stream-based readers to high-level utility methods. Understanding the available tools—such as FileReader, BufferedReader, Scanner, and the modern Files class—enables developers to choose the most efficient and appropriate method for their needs. This guide covers core techniques for reading files in Java, including character and line-by-line reading, handling exceptions, and best practices for resource management.
1. Key Classes for File Reading
Java offers several classes in the java.io and java.nio.file packages:
| Class / Method | Package | Use Case |
|---|---|---|
FileReader | java.io | Simple character reading (no buffering) |
BufferedReader | java.io | Efficient line-by-line reading |
Scanner | java.util | Token-based parsing (e.g., numbers, words) |
Files.readAllLines() | java.nio.file | Read entire file into a List<String> |
Files.readString() (Java 11+) | java.nio.file | Read entire file as a single String |
FileInputStream | java.io | Reading raw bytes (not covered here—focus is on text) |
Note: This guide focuses on text file reading.
2. Reading a File Line by Line Using BufferedReader
The most common and efficient way to read large text files is with BufferedReader wrapped around a FileReader.
Basic Example
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
String filePath = "data.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
}
}
Key Points
- Uses try-with-resources to ensure the file is closed automatically.
readLine()returnsnullwhen the end of the file is reached.- Efficient for large files due to internal buffering.
3. Reading Entire File at Once
A. Using Files.readAllLines() (Java 7+)
Reads all lines into a List<String>.
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.List;
public class ReadAllLinesExample {
public static void main(String[] args) {
try {
List<String> lines = Files.readAllLines(Paths.get("data.txt"));
for (String line : lines) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("File read error: " + e.getMessage());
}
}
}
Best for: Small to medium-sized files (loads entire file into memory).
B. Using Files.readString() (Java 11+)
Reads the entire file as a single String.
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
public class ReadStringExample {
public static void main(String[] args) {
try {
String content = Files.readString(Paths.get("data.txt"));
System.out.println(content);
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
Note: Preserves line breaks (
\n,\r\n) as they appear in the file.
4. Reading with Scanner
Useful when parsing structured data (e.g., numbers, tokens separated by whitespace).
Example: Reading Words or Numbers
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ScannerExample {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(new File("numbers.txt"))) {
while (scanner.hasNext()) {
if (scanner.hasNextInt()) {
int num = scanner.nextInt();
System.out.println("Number: " + num);
} else {
String word = scanner.next();
System.out.println("Word: " + word);
}
}
} catch (FileNotFoundException e) {
System.err.println("File not found: " + e.getMessage());
}
}
}
Limitation: Less efficient for line-by-line reading compared to
BufferedReader.
5. Specifying Character Encoding
By default, file readers use the platform’s default encoding (e.g., UTF-8 on most modern systems). To ensure portability, explicitly specify encoding (e.g., UTF-8).
With BufferedReader
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"),
StandardCharsets.UTF_8))) {
// Read lines...
}
With Files (Java 11+)
String content = Files.readString(Paths.get("data.txt"), StandardCharsets.UTF_8);
List<String> lines = Files.readAllLines(Paths.get("data.txt"), StandardCharsets.UTF_8);
Best Practice: Always specify encoding to avoid platform-dependent behavior.
6. Handling Common Exceptions
FileNotFoundException: Thrown if the file does not exist.IOException: General I/O error (e.g., disk full, permission denied).
Always handle or declare these exceptions. Use try-with-resources to prevent resource leaks.
7. Best Practices
- Prefer
BufferedReaderfor line-by-line reading of large files. - Use
Files.readString()orreadAllLines()for small files (simpler code). - Always specify character encoding (e.g.,
StandardCharsets.UTF_8). - Use try-with-resources to ensure files are closed automatically.
- Avoid loading very large files entirely into memory—stream line by line instead.
- Validate file existence and permissions before reading in production systems.
- Log meaningful error messages for debugging and monitoring.
8. Performance Comparison
| Method | Memory Usage | Speed | Use Case |
|---|---|---|---|
BufferedReader | Low | Fast | Large files, streaming |
Files.readAllLines() | High | Fast | Small files, random access to lines |
Files.readString() | High | Fast | Small files, full-text processing |
Scanner | Medium | Slower | Token-based parsing |
Conclusion
Reading files in Java can be accomplished through multiple approaches, each suited to different scenarios. For most applications, BufferedReader with try-with-resources offers the best balance of efficiency and control, especially for large or unknown-sized files. For simpler cases involving small files, the Files utility methods (readAllLines, readString) provide concise, readable solutions. By specifying character encoding, handling exceptions properly, and following resource management best practices, developers can build reliable and maintainable file-reading functionality. Whether processing logs, loading configurations, or ingesting user data, mastering file reading is essential for robust Java application development.