Why File I/O Is an Essential Java Skill

Almost every real-world Java application needs to read or write files — whether that's loading configuration, processing CSV data, generating reports, or writing logs. Java provides several ways to handle file input/output (I/O), and choosing the right approach matters for readability, performance, and correctness.

In this guide, we cover the most common and practical methods available in modern Java.

Method 1: Using java.nio.file.Files (Recommended for Modern Java)

The java.nio.file package (introduced in Java 7) is the preferred way to work with files today. It's concise, readable, and handles edge cases well.

Reading a File

import java.nio.file.*;
import java.util.List;

public class FileReader {
    public static void main(String[] args) throws Exception {
        Path path = Path.of("data.txt");
        List<String> lines = Files.readAllLines(path);
        lines.forEach(System.out::println);
    }
}

Writing to a File

import java.nio.file.*;
import java.util.List;

public class FileWriter {
    public static void main(String[] args) throws Exception {
        Path path = Path.of("output.txt");
        List<String> content = List.of("Line 1", "Line 2", "Line 3");
        Files.write(path, content);
    }
}

Tip: Use Files.writeString() if you're writing a single string directly.

Method 2: Using BufferedReader and BufferedWriter

For large files, reading all lines into memory at once can be inefficient. A BufferedReader reads line by line, keeping memory usage low.

import java.io.*;

public class BufferedFileReader {
    public static void main(String[] args) throws Exception {
        try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}

Always use try-with-resources to ensure the file is closed automatically, even if an exception occurs.

Method 3: Using FileInputStream for Binary Files

For non-text files (images, PDFs, audio), use FileInputStream and FileOutputStream:

import java.io.*;

public class BinaryFileCopy {
    public static void main(String[] args) throws Exception {
        try (FileInputStream in = new FileInputStream("photo.jpg");
             FileOutputStream out = new FileOutputStream("photo_copy.jpg")) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
    }
}

Comparison: Which Method Should You Use?

MethodBest ForMemory Usage
Files.readAllLines()Small to medium text filesHigher (loads all)
BufferedReaderLarge text filesLow (streams)
FileInputStreamBinary filesConfigurable
Files.lines() (Stream)Lazy processing of textLow (lazy stream)

Common Pitfalls to Avoid

  • Not closing streams — always use try-with-resources
  • Hardcoding file paths — use Path.of() or relative paths for portability
  • Ignoring exceptions — at minimum, log them; don't swallow IOException
  • Wrong charset — specify StandardCharsets.UTF_8 when needed

Summary

For most tasks, start with java.nio.file.Files — it's clean and modern. Switch to BufferedReader when you need to handle large files efficiently. Use byte streams for binary data. Master these patterns and you'll handle file I/O confidently in any Java project.