Skip to content
Java file handling 6 min read

Write to a File

Writing to a file is one of the most common tasks in Java — whether you’re saving application logs, exporting data, or persisting configuration. Java gives you several APIs to do it, ranging from the classic java.io streams to the modern java.nio.file.Files helpers. This page walks through each approach so you can pick the right one for your situation.

Using FileWriter

FileWriter is the simplest way to write text to a file. It opens (or creates) a file and lets you write String data directly.

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterExample {
    public static void main(String[] args) {
        try (FileWriter fw = new FileWriter("output.txt")) {
            fw.write("Hello, Java!\n");
            fw.write("Writing to a file is easy.");
            System.out.println("File written successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Output:

File written successfully.

output.txt will contain:

Hello, Java!
Writing to a file is easy.

Note: The try-with-resources block automatically closes the writer, flushing any buffered data. Always use it to avoid resource leaks.

Warning: By default, FileWriter overwrites an existing file. To append instead, use new FileWriter("output.txt", true).


Wrapping a FileWriter in a BufferedWriter batches writes in memory before flushing to disk. This is significantly faster when writing many lines, because it reduces the number of actual OS write calls.

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

public class BufferedWriterExample {
    public static void main(String[] args) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"))) {
            bw.write("Line 1: Application started");
            bw.newLine();
            bw.write("Line 2: Processing data...");
            bw.newLine();
            bw.write("Line 3: Done.");
            System.out.println("Log file written.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

bw.newLine() writes the platform-appropriate line separator (\n on Linux/macOS, \r\n on Windows), making your output portable.

Tip: Prefer BufferedWriter over bare FileWriter whenever you write more than one line. The internal buffer (default 8 KB) makes a noticeable difference in performance.


Appending to an Existing File

Both FileWriter and BufferedWriter support an append flag. Pass true as the second constructor argument:

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

public class AppendToFile {
    public static void main(String[] args) {
        // Appends to "log.txt" instead of overwriting it
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt", true))) {
            bw.write("New log entry appended.");
            bw.newLine();
            System.out.println("Entry appended.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Using PrintWriter

PrintWriter adds println(), printf(), and format() methods on top of FileWriter, making it feel like writing to System.out. It’s a great fit for formatted text output.

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriterExample {
    public static void main(String[] args) {
        try (PrintWriter pw = new PrintWriter(new FileWriter("report.txt"))) {
            pw.println("=== Sales Report ===");
            pw.printf("%-15s %10s%n", "Product", "Revenue");
            pw.printf("%-15s %10.2f%n", "Widget A", 4250.75);
            pw.printf("%-15s %10.2f%n", "Gadget B", 8120.00);
            System.out.println("Report written.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

report.txt content:

=== Sales Report ===
Product              Revenue
Widget A             4250.75
Gadget B             8120.00

Warning: PrintWriter swallows IOException silently by default. Call pw.checkError() after writing to detect failures, or wrap a FileWriter inside it as shown above so the outer try-catch stays in play.


Using FileOutputStream (Binary / Byte-Level Writing)

When you need to write raw bytes — images, serialized data, or any binary content — use FileOutputStream directly.

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamExample {
    public static void main(String[] args) {
        byte[] data = "Binary data: Hello".getBytes();
        try (FileOutputStream fos = new FileOutputStream("data.bin")) {
            fos.write(data);
            System.out.println("Bytes written: " + data.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

For text written as bytes, explicitly specify a charset to avoid platform-dependent encoding:

fos.write("Hello, World!".getBytes(java.nio.charset.StandardCharsets.UTF_8));

Using NIO Files.write() and Files.writeString() (Java 7+ / Java 11+)

The modern java.nio.file.Files class offers convenient one-liner methods that handle opening, writing, and closing for you. For more on the NIO.2 API, see NIO.2: Path & Files.

Files.write() — write a list of lines (Java 7+)

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class NioWriteLines {
    public static void main(String[] args) throws IOException {
        List<String> lines = List.of("Alpha", "Beta", "Gamma");
        Path path = Path.of("items.txt");
        Files.write(path, lines, StandardCharsets.UTF_8);
        System.out.println("Lines written: " + lines.size());
    }
}

Files.writeString() — write a single string (Java 11+)

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class NioWriteString {
    public static void main(String[] args) throws IOException {
        String content = "This is the entire file content.\nSecond line here.";
        Files.writeString(Path.of("note.txt"), content);
        System.out.println("File written.");
    }
}

Appending with NIO

Pass StandardOpenOption.APPEND to avoid overwriting:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class NioAppend {
    public static void main(String[] args) throws IOException {
        Files.writeString(
            Path.of("note.txt"),
            "\nAppended line.",
            StandardOpenOption.APPEND
        );
        System.out.println("Appended.");
    }
}

Tip: For small files that fit comfortably in memory, Files.writeString() is the cleanest option available in modern Java. For large files or streaming scenarios, stick with BufferedWriter.


Choosing the Right Approach

ScenarioRecommended API
Simple text file (small)Files.writeString() (Java 11+)
Write many lines of textBufferedWriter wrapping FileWriter
Formatted text (printf-style)PrintWriter
List of lines at onceFiles.write(path, lines, charset)
Raw binary / byte dataFileOutputStream (optionally buffered)
Append to existing log fileAny writer with append = true flag

Under the Hood

When you write to a file in Java, data travels through several layers before hitting physical storage:

  1. Java layer — your write() call fills a byte buffer inside the writer object.
  2. JVM / OS boundary — when the buffer is full (or flush() / close() is called), the JVM issues a write() syscall, transferring data to the OS kernel page cache.
  3. Kernel page cache — the OS keeps the data in memory and marks those pages as “dirty.” The kernel’s pdflush / writeback threads periodically flush dirty pages to disk.
  4. Physical storage — data is finally written to the storage device.

This means that even after close() returns, your data may still be in the OS cache and not durably on disk. For mission-critical durability (e.g., financial records), call FileOutputStream.getFD().sync() or open the file with StandardOpenOption.SYNC / StandardOpenOption.DSYNC to force an fsync syscall.

BufferedWriter adds an extra in-process buffer (typically 8 192 bytes) between your code and the OS. Writing 10 000 one-character strings without BufferedWriter generates 10 000 syscalls; with BufferedWriter it might generate as few as two. The performance difference is real and measurable.

Encoding also matters. FileWriter in Java 11 and earlier defaults to the platform default charset, which varies between Windows (often CP-1252) and Linux (UTF-8). From Java 17 the no-arg FileWriter(String) constructor still uses the default charset, so always pass an explicit charset when portability matters:

new FileWriter("file.txt", java.nio.charset.StandardCharsets.UTF_8)

Or use Files.writeString(), which always defaults to UTF-8.


Last updated June 13, 2026
Was this helpful?