In Java 11 and later, the simplest way to write text to a file is Files.writeString(Path.of("out.txt"), text), which creates the file if it does not exist and replaces everything in it if it does. Every example on this page runs right here on a small disk that lives in your browser, so you can press Run and watch the file fill up. After the short way come appending, the classic writers (FileWriter, BufferedWriter and PrintWriter), why a file can come out empty, and how to create files and folders.
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { Path file = Path.of("out.txt"); Files.writeString(file, "Hello, file!\nThis is line two.\n"); System.out.print(Files.readString(file)); }}Step to the end and the memory view shows out.txt holding two lines; the last line of the program reads the file back, so the text also appears as output. Path.of turns the name into a Path, Java's object for where a file is, and writeString opens the file, writes the text and closes the file in that one call, so there is nothing left for you to close.
writeString writes exactly the characters you give it and adds no line break of its own, so end each line with \n. Writing can fail, on a full disk or in a read-only folder, and the failure is an IOException, a checked exception that Java makes you catch or declare. Here main declares it with throws IOException. writeString arrived in Java 11. On Java 8, Files.write(path, text.getBytes(StandardCharsets.UTF_8)) does the same job.
Write a list of lines with Files.write#
When your text is a list of lines, Files.write(path, lines) writes each element and ends it with a line break, the last one included. This program builds a shopping list and saves it:
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.util.ArrayList;import java.util.List; public class Main { public static void main(String[] args) throws IOException { String[] items = {"bread", "eggs", "coffee"}; List<String> lines = new ArrayList<>(); for (String item : items) { lines.add("- " + item); } Files.write(Path.of("shopping.txt"), lines); System.out.print(Files.readString(Path.of("shopping.txt"))); }}Like writeString, Files.write creates the file or replaces it, and closes it before it returns. The list can come from anywhere: a loop, as here, or the lines you read from another file.
Append to a file instead of replacing it#
Every way of writing a file replaces what was in it, unless you ask to append, which adds your text after the old contents. With the Files methods, pass StandardOpenOption.CREATE and StandardOpenOption.APPEND. With FileWriter, pass true as a second argument. This log.txt starts with a line from Sunday, and the program adds a line each way:
import java.io.FileWriter;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.StandardOpenOption; public class Main { public static void main(String[] args) throws IOException { Path log = Path.of("log.txt"); Files.writeString(log, "Monday: opened\n", StandardOpenOption.CREATE, StandardOpenOption.APPEND); try (FileWriter writer = new FileWriter("log.txt", true)) { writer.write("Monday: closed\n"); } System.out.print(Files.readString(log)); }}Sunday: opened
Sunday's line stays, and the two new lines land after it. APPENDon its own means “add to a file that exists”, so on a first run, before the file exists, it throws java.nio.file.NoSuchFileException: log.txt. CREATE makes the file when it is missing. new FileWriter("log.txt", true) creates a missing file by itself.
| Way | Replace the file | Add to the end |
|---|---|---|
Files.writeString | writeString(path, text) | writeString(path, text, CREATE, APPEND) |
Files.write | write(path, lines) | write(path, lines, CREATE, APPEND) |
Files.newBufferedWriter | newBufferedWriter(path) | newBufferedWriter(path, CREATE, APPEND) |
FileWriter | new FileWriter("log.txt") | new FileWriter("log.txt", true) |
BufferedWriter, PrintWriter | Wrap new FileWriter("log.txt") | Wrap new FileWriter("log.txt", true) |
true or the APPEND when you add to a log, and the whole log is gone.Write to a file with FileWriter and BufferedWriter#
FileWriter is the classic class for writing text to a file: open it with a file name, call write, close it. Wrapped in a BufferedWriter, it gains newLine(), which ends the current line, and its many small writes are gathered into a few large ones, which is faster. This program writes a list of names, one per line:
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { String[] names = {"Aisha", "Ben", "Carmen"}; try (BufferedWriter writer = new BufferedWriter(new FileWriter("names.txt"))) { for (String name : names) { writer.write(name); writer.newLine(); } } System.out.print(Files.readString(Path.of("names.txt"))); }}Step through it and watch the Files section of the memory view. names.txt appears, empty, the moment FileWriter opens it. Each write and newLine()only adds to the writer's buffer, and all three names reach the file together when the try block ends and closes the writer. write never adds a line break by itself: without the newLine() calls the file would hold AishaBenCarmen.
The writer is declared in the parentheses after try, a form called try-with-resources: Java closes it when the block ends, even if an exception ends it early. Files.newBufferedWriter(Path.of("names.txt")) gives you the same kind of writer from a Path.
write has a version that takes an int and treats it as a character code, so writer.write(91) writes [, the character with code 91. Turn a number into text first, as in writer.write("Score: " + 91), or use a PrintWriter, whose println(91) writes the digits.Write to a file with PrintWriter#
PrintWriter gives a file the print, println and printf methods you know from System.out. printf lines text up in columns: %-6s pads a string with spaces to 6 characters, %5d writes a whole number right-aligned in 5, and %n ends the line.
import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { String[] names = {"Ada", "Grace", "Alan"}; int[] scores = {91, 88, 79}; try (PrintWriter out = new PrintWriter(new FileWriter("scores.txt"))) { out.println("Name Score"); for (int i = 0; i < names.length; i++) { out.printf("%-6s %5d%n", names[i], scores[i]); } } System.out.print(Files.readString(Path.of("scores.txt"))); }}The file gets a heading and three rows with the scores lined up under it. Use the form new PrintWriter(new FileWriter(name)), because the FileWriter inside can take true to append. new PrintWriter("scores.txt") works too, but always replaces the file. Unlike the other writers, PrintWriter's methods never throw an IOException, so a failed write goes unnoticed unless you call checkError(), which returns true once anything has gone wrong.
Why is my file empty? Close or flush the writer#
A writer keeps your text in a buffer, a holding area in memory, and sends it to the disk in large batches, because each trip to the disk is slow. The text reaches the file only when the buffer fills up, when you call flush(), or when you call close(), which flushes first. This program never closes its writer:
import java.io.FileWriter;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { FileWriter writer = new FileWriter("diary.txt"); writer.write("Dear diary,\n"); writer.write("Today I wrote my first file.\n"); System.out.println("Bytes in diary.txt: " + Files.size(Path.of("diary.txt"))); }}Both write calls succeed, yet the program prints Bytes in diary.txt: 0. Step to the end and the narration says the text was still in writer's buffer when the program finished, so it never reached the file. Open the program in the playground (the button next to Run), add writer.close(); above the println, and run it again: the file then holds 41 bytes. A plain FileWriter buffers like this, and so do BufferedWriter and PrintWriter.
Key ideaYour text is in the file only after
flush()orclose(). Open every writer in try-with-resources and Java closes it for you, even when an exception ends the block early.
write on a FileWriter or BufferedWriter throws java.io.IOException: Stream closed.Writing files walks through buffers, flush() and close() step by step.
How to create a file in Java#
Usually you need no separate step: Files.writeString, Files.write, FileWriter and PrintWriter all create the file when it does not exist. Create a file on its own when you want it empty, or when you must not replace a file that is already there. File.createNewFile() returns true if it made the file and false if the file was already there, while Files.createFile throws a FileAlreadyExistsException. This disk starts with a notes.txt:
import java.io.File;import java.io.IOException;import java.nio.file.FileAlreadyExistsException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { File todo = new File("todo.txt"); System.out.println("Created todo.txt? " + todo.createNewFile()); System.out.println("Created it again? " + todo.createNewFile()); try { Files.createFile(Path.of("notes.txt")); } catch (FileAlreadyExistsException e) { System.out.println("Not created: " + e); } }}Buy milk
Each of them checks for the file and creates it in a single step, so no other program can create the same file in between. To write a file only if it is new, pass StandardOpenOption.CREATE_NEW to Files.writeString: it creates the file and writes your text, or throws FileAlreadyExistsException and leaves the old file alone.
Create missing folders with Files.createDirectories#
The writing methods create files, never folders. Writing to reports/2026/march.txt when there is no reports folder stops with java.nio.file.NoSuchFileException: reports/2026/march.txt, which is confusing until you see that the missing piece is the folder. Files.createDirectories makes a folder, and any folders above it that are missing, first:
import java.io.IOException;import java.nio.file.Files;import java.nio.file.Path; public class Main { public static void main(String[] args) throws IOException { Path folder = Path.of("reports", "2026"); Files.createDirectories(folder); Path file = folder.resolve("march.txt"); Files.writeString(file, "Sales: 12\n"); System.out.println("Wrote " + file); }}It prints Wrote reports/2026/march.txt, with backslashes on Windows. createDirectories does nothing for folders that already exist, so it is safe to call on every run, and resolveadds a name to the end of the folder's path.
Which way should you use?#
| Way | Best for | Closes the file itself? |
|---|---|---|
Files.writeString(path, text) | A whole file from one String | Yes |
Files.write(path, lines) | A whole file from a list of lines | Yes |
BufferedWriter around a FileWriter, or Files.newBufferedWriter | Writing line by line in a loop, big files, a log that stays open | No, use try-with-resources |
PrintWriter | Formatted text with println and printf | No, use try-with-resources |
FileWriter on its own | A few short writes; wrap it in a BufferedWriter for more | No, use try-with-resources |
When the whole text fits in memory, Files.writeString or Files.write is the shortest choice and leaves no writer to forget. Use a writer when the text comes out bit by bit. Path and Files covers the Files methods in depth, and how to read a file in Java shows the way back in.
Character encoding and line endings#
A file stores bytes, and a character encoding is the rule that turns letters into bytes. The Files methods write UTF-8 unless you pass another encoding. Since Java 18 (JEP 400), FileWriter and PrintWriterdefault to UTF-8 too, on every operating system. Before Java 18 they used the computer's own default, which on many Windows machines was not UTF-8, so older code names the encoding: new FileWriter("out.txt", StandardCharsets.UTF_8), a constructor added in Java 11.
newLine(), println, %n and Files.writeend each line with your system's line separator: \n on macOS and Linux, and the two characters \r\n on Windows. A \n you put inside a string stays a single \neverywhere. When you need the system's separator in a string of your own, System.lineSeparator() returns it.
Questions people ask#
- How do I write a String to a file in Java?
- Call
Files.writeString(Path.of("out.txt"), text), which arrived in Java 11. It creates the file if it is missing, replaces anything already in it, and closes the file before it returns. - How do I append to a file instead of overwriting it?
- Pass
StandardOpenOption.CREATEandStandardOpenOption.APPENDtoFiles.writeString, or opennew FileWriter("log.txt", true). Without them, every way of writing a file replaces what was in it. - Why is my file empty after writing to it?
- The writer was never closed or flushed, so the text was still waiting in its buffer in memory when the program ended. Open the writer in a try-with-resources block, or call
close()when you finish writing. - How do I create a new file in Java?
- Writing to a file that does not exist creates it, so
Files.writeStringornew FileWriter(name)is usually enough. To create an empty file only if it is new, callnew File(name).createNewFile(), which returnsfalseif the file already exists, orFiles.createFile(path), which throwsFileAlreadyExistsException. - How do I write to a file using PrintWriter?
- Open
new PrintWriter(new FileWriter("report.txt"))in a try-with-resources block and callprintlnorprintfon it, as you would onSystem.out. Its methods never throwIOException, so callcheckError()when you need to know whether a write failed. - What is the difference between FileWriter and BufferedWriter?
FileWriterwrites characters to a file.BufferedWriterwraps another writer, gathers many small writes into fewer large ones and addsnewLine(), so the usual form isnew BufferedWriter(new FileWriter(name)).
Further reading#
- The Files class in the Java SE 25 API documentation, with writeString, write, createFile and createDirectories
- StandardOpenOption in the Java SE 25 API documentation, with APPEND, CREATE and CREATE_NEW
- BufferedWriter in the Java SE 25 API documentation, with newLine, flush and close
- JEP 400: UTF-8 by Default, the change that made UTF-8 the default in Java 18