File Reading in Java: A Complete Guide

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 / MethodPackageUse Case
FileReaderjava.ioSimple character reading (no buffering)
BufferedReaderjava.ioEfficient line-by-line reading
Scannerjava.utilToken-based parsing (e.g., numbers, words)
Files.readAllLines()java.nio.fileRead entire file into a List<String>
Files.readString() (Java 11+)java.nio.fileRead entire file as a single String
FileInputStreamjava.ioReading 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() returns null when 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 BufferedReader for line-by-line reading of large files.
  • Use Files.readString() or readAllLines() 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

MethodMemory UsageSpeedUse Case
BufferedReaderLowFastLarge files, streaming
Files.readAllLines()HighFastSmall files, random access to lines
Files.readString()HighFastSmall files, full-text processing
ScannerMediumSlowerToken-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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper