Skip to main content

How to Write to Files in Java

A hands-on guide to every major way of writing files in Java — and how to pick the right one

Sooner or later, almost every Java program needs to write something to disk — a log entry, an exported report, a config file, a saved user session. The good news is Java gives you plenty of ways to do it. The less good news is that "plenty of ways" can feel overwhelming if you're not sure which class actually fits your situation. This guide walks through the main options, with working examples for each, so you can pick the right tool instead of just the first one you remember.

The Building Blocks of Java File Writing

File writing in Java is fundamentally an I/O (input/output) operation, and most of the classes you'll use live in the java.io package, with a few modern additions in java.nio.file. The core players are:

  • FileWriter — writes character data directly to a file. Simple, but with no built-in buffering.
  • BufferedWriter — wraps another writer and buffers the output, cutting down on the number of actual disk writes.
  • PrintWriter — adds convenience methods like println() and printf() for writing formatted text.
  • FileOutputStream — writes raw bytes, which makes it the go-to choice for binary data.
  • Files (from java.nio.file) — a modern, streamlined API for reading and writing files in just a line or two.

Each one solves a slightly different problem. Let's go through them.

Quick Comparison: Which Writer Should You Use?

Class Best For Buffered? Notes
FileWriter Quick, simple text writes No Fine for small, one-off writes
BufferedWriter Frequent or large text writes Yes Reduces disk I/O overhead
PrintWriter Formatted output (printf, println) Optional Most convenient for readable output
FileOutputStream Binary data No Pair with BufferedOutputStream for large files
Files.write() Small files, one-shot writes N/A Shortest syntax, modern NIO API

1. Writing to a File with FileWriter

FileWriter is the most basic way to put text into a file — good for simple, low-volume writes.

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

public class FileWriteExample {
    public static void main(String[] args) {
        try {
            FileWriter writer = new FileWriter("output.txt");
            writer.write("Hello, world!");
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

What's happening here:

  • new FileWriter("output.txt") opens the file for writing — and creates it automatically if it doesn't already exist.
  • writer.write(...) sends the string to the file.
  • writer.close() releases the underlying resource. Skipping this step is a common source of bugs, since unflushed data can be lost if the program exits before closing.

The catch with plain FileWriter is that every call to write() can trigger a separate disk operation, which gets slow fast if you're writing a lot of data. That's where BufferedWriter comes in.

2. Writing Efficiently with BufferedWriter

Wrapping a BufferedWriter around a FileWriter batches up writes in memory and flushes them to disk in larger chunks, which is noticeably faster for anything beyond a trivial amount of text.

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

public class BufferedWriterExample {
    public static void main(String[] args) {
        try {
            BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));
            writer.write("Using BufferedWriter to write.");
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Here's a more realistic version that writes multiple lines, using newLine() for platform-correct line breaks:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Arrays;

public class MultiLineWriter {
    public static void main(String[] args) {
        List<String> lines = Arrays.asList("First line", "Second line", "Third line");

        try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
            for (String line : lines) {
                writer.write(line);
                writer.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

If you're writing more than a handful of lines, BufferedWriter should generally be your default over a raw FileWriter.

3. Writing Formatted Text with PrintWriter

When you need output that looks like something a human would want to read — formatted numbers, multiple data types on one line — PrintWriter is the most convenient option, since it supports the same printf()-style formatting you'd use for console output.

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

public class PrintWriterExample {
    public static void main(String[] args) {
        try {
            PrintWriter writer = new PrintWriter(new FileWriter("output.txt"));
            writer.println("This is a line.");
            writer.printf("And this is a number: %d%n", 100);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

You can also combine PrintWriter with BufferedWriter to get both formatting convenience and buffered performance:

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

public class FormattedReportWriter {
    public static void main(String[] args) {
        try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("report.txt")))) {
            writer.println("Sales Report");
            writer.println("------------");
            writer.printf("Product: %-15s Revenue: $%,.2f%n", "Widget", 4523.75);
            writer.printf("Product: %-15s Revenue: $%,.2f%n", "Gadget", 1899.10);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

This combination is common in real applications — reports, logs, and exports where readability matters.

4. Appending to a File Instead of Overwriting It

By default, most writers overwrite a file's existing content. If you want to add to a file instead — for logging, for example — pass true as a second argument to FileWriter's constructor:

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

public class AppendFileExample {
    public static void main(String[] args) {
        try {
            FileWriter writer = new FileWriter("output.txt", true);
            writer.write("Appending text.");
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

That true flag is easy to overlook but makes a big difference — without it, every run of your program would wipe out the previous content instead of adding to it. This pattern is especially useful for simple logging:

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

public class SimpleLogger {
    public static void log(String message) {
        try (PrintWriter writer = new PrintWriter(new FileWriter("app.log", true))) {
            writer.println(LocalDateTime.now() + " - " + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        log("Application started");
        log("User logged in");
    }
}

5. Writing Binary Data with FileOutputStream

Text writers like FileWriter are built for characters, not raw bytes — so for binary data (images, serialized objects, custom formats), you'll want FileOutputStream instead.

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

public class FileOutputStreamExample {
    public static void main(String[] args) {
        byte[] data = "Binary-safe content".getBytes();

        try (FileOutputStream fos = new FileOutputStream("output.bin")) {
            fos.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

For larger binary writes, wrap it in a BufferedOutputStream the same way you'd buffer a text writer:

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

public class BufferedBinaryWriter {
    public static void main(String[] args) {
        byte[] chunk = new byte[1024];

        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("large.bin"))) {
            for (int i = 0; i < 100; i++) {
                bos.write(chunk);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. The Modern Shortcut: Files.write()

If you're on Java 7 or later, the java.nio.file.Files class offers a much shorter way to write small files in a single call:

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

public class NioFileWriter {
    public static void main(String[] args) {
        String content = "Written using the NIO Files API.";

        try {
            Files.write(Paths.get("output.txt"), content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

You can control append-versus-overwrite behavior explicitly with StandardOpenOption:

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

public class NioAppendExample {
    public static void main(String[] args) {
        String content = "Appended with NIO.\n";

        try {
            Files.write(
                Paths.get("output.txt"),
                content.getBytes(),
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

For small, straightforward writes, this is often the cleanest option available — no explicit writer object to manage at all.

7. Handling Resources the Right Way: try-with-resources

Every writer and stream shown above implements AutoCloseable, which means try-with-resources should be your default pattern for writing files, since it guarantees the resource gets closed even if an exception occurs mid-write.

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

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        try (FileWriter writer = new FileWriter("output.txt")) {
            writer.write("Try with resources ensures closure.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Compare that to the manual cleanup it replaces:

// Old style — verbose, and easy to leak a resource if you forget the finally block
FileWriter writer = null;
try {
    writer = new FileWriter("output.txt");
    writer.write("Manual cleanup required.");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (writer != null) {
        try {
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The try-with-resources version is shorter, harder to get wrong, and works with multiple resources at once — just separate them with semicolons inside the parentheses.

Choosing the Right Method: A Quick Decision Guide

  • Writing a small amount of plain text?FileWriter or Files.write()
  • Writing a lot of text, or writing frequently?BufferedWriter
  • Need formatted output (numbers, multiple lines, printf-style)?PrintWriter
  • Logging or adding to an existing file? → Any writer with the append flag (or StandardOpenOption.APPEND)
  • Writing binary data?FileOutputStream, buffered for large files
  • Want the shortest possible code for a one-off write?Files.write()

Final Thoughts

Writing files in Java isn't complicated once you know which tool matches the job. For everyday text output, BufferedWriter and PrintWriter will cover the vast majority of cases. For binary data, FileOutputStream is the right call. And for quick, small writes, the modern Files.write() method is hard to beat for sheer simplicity.

Whichever class you reach for, make try-with-resources a habit — it's a small change that quietly prevents a whole category of resource-leak bugs. Once writing feels natural, pairing it with the file-reading techniques you already know rounds out a solid foundation for handling data in Java.

Popular posts from this blog

How to Check if Someone is Connected to Your Machine in Linux

Picture this: you glance at your system monitor and notice your CPU is humming along even though you're not running anything demanding. Or maybe your internet feels sluggish for no obvious reason. A small, uneasy thought creeps in — is someone else on my machine right now? For Linux users, this isn't something you have to wonder about. Linux ships with a powerful set of built-in tools that let you see exactly who's connected, who's logged in, and what your network is doing at any given moment. You don't need to be a security expert to use them — you just need to know where to look. This guide walks you through the practical, no-nonsense steps to check for unauthorized connections on your Linux system, with real commands you can run right now. Why Monitoring Network Connections Matters Every device on a network — including your own Linux machine — communicates using an IP address. When another device or user connects to your system, that connection shows up as a trac...

How to Set Up a Linux Web Server and Host an HTML Page Easily

Setting up a web server on Linux means spending a fair amount of time in the terminal — Linux leans heavily on the command line rather than clicking through menus, so you'll be typing out instructions more often than not.  If you're new to this, it can feel a little intimidating at first, but the good news is you don't need to become a Linux wizard overnight. A handful of core commands will get you surprisingly far. A few you'll lean on constantly: cd — move between directories ls — see what's in the current directory mkdir — create a new folder nano or vim — edit files right there in the terminal sudo — run something with administrator privileges Get comfortable with these and you'll be able to navigate around, tweak configuration files, and install software without much trouble. You don't need to memorize everything — you just need to be confident enough to follow along with clear instructions, which is exactly what this guide aims to give you....

Linux Network Troubleshooting

If you've spent any time as a sysadmin — or honestly, just as someone who's had to fix their own home network at 11pm — you know that connectivity issues are one of the most common headaches out there. The good news is that a handful of core tools and a methodical approach can take you from "why isn't this working" to a root cause pretty quickly.  This guide walks through the essentials: configuring interfaces, managing routes, and diagnosing problems when things go sideways. Configuring Network Interfaces Your network interfaces are the actual bridge between your machine and the outside world, so getting them configured correctly is step one for any kind of reliable connectivity. Doing It Manually ifconfig is the old-school, tried-and-true tool for this on Unix-like systems. To see everything currently configured, run: ifconfig -a If you need to manually set up a specific interface — assigning an IP, a netmask, and bringing it online — it looks like this: ifconf...