Java Read and Write File

In Java, handling file operations is a fundamental skill for tasks like processing logs, saving user settings, or managing data exports. While there are many ways to handle I/O (Input/Output), using buffered streams is the most common approach for text files because it minimizes the number of actual disk reads and writes, making your application much faster.

To read from and write to a file in Java, you can use classes like BufferedReader and BufferedWriter. These classes act as a "wrapper" around lower-level classes like FileReader and FileWriter to provide efficient, line-based processing.

Developer Tip: Use character-based classes (Reader/Writer) for text data and byte-based classes (InputStream/OutputStream) for binary data like images or PDFs.

Modern Java Implementation

Here is a robust example of how to read content from one file and copy it to another. This version uses the Try-With-Resources statement, which is the industry standard for ensuring files are closed automatically, even if an error occurs.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class ReadWriteFileExample {
    public static void main(String[] args) {
        // Define file paths
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        // Try-with-resources ensures both reader and writer close automatically
        try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
             BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile))) {

            String line;
            // Read input file line by line until reaching the end (null)
            while ((line = reader.readLine()) != null) {
                // Example logic: Modify the text before writing (optional)
                String processedLine = line.toUpperCase();
                
                // Write the line to the output file
                writer.write(processedLine);
                
                // Explicitly add a line break
                writer.newLine();
            }

            System.out.println("Processing complete. Check " + outputFile);

        } catch (IOException e) {
            System.err.println("Error processing files: " + e.getMessage());
        }
    }
}
Best Practice: Always use the Try-With-Resources block (introduced in Java 7) when working with files. It prevents memory leaks by automatically closing the file streams once the block is finished.

Understanding the Components

In the example above, several important classes work together to handle the data flow:

  • FileReader / FileWriter: These are "bridge" classes that connect your code to the actual file on the hard drive. However, they are relatively slow on their own because they access the disk for every single character.
  • BufferedReader / BufferedWriter: These wrap around the File classes and create a memory buffer. Instead of reading one character at a time, they read a large "chunk" of data into memory at once, which is significantly more efficient.
  • readLine(): This method is incredibly useful as it allows you to process data one line at a time rather than loading a massive 2GB file into your RAM all at once.
  • newLine(): This is preferred over hardcoding \n because it automatically uses the correct line separator for the operating system your code is running on (Windows uses \r\n, while Linux/macOS use \n).
Watch Out: By default, FileWriter will overwrite the target file. If you want to add content to the end of an existing file instead of erasing it, use new FileWriter(outputFile, true).
Common Mistake: Beginners often forget that readLine() strips away the newline character. If you don't call writer.newLine(), your entire output file will end up as one single, massive line of text.

Real-World Example: Log Filtering

Imagine you have a large server log file and you only want to extract lines that contain the word "ERROR". You can easily modify the logic inside the while loop to filter data:

while ((line = reader.readLine()) != null) {
    if (line.contains("ERROR")) {
        writer.write(line);
        writer.newLine();
    }
}

This simple pattern is the basis for many data processing tools used in backend development.

Developer Tip: For modern applications, also check out the java.nio.file.Files class. For simple tasks, Files.readAllLines() or Files.writeString() can handle these operations in a single line of code!