Java Writing Files
Table of Contents + β
In the last lesson you learned about Java reading files. Reading is only half of it. To save data so it lasts, you need to write files. Letβs learn how.
π€ Why write files?
Think of a programβs memory like a whiteboard. While the program runs, your numbers and names sit there. The moment the program closes, the whiteboard is wiped clean. A file is like a notebook. What you write stays, even after the program ends and the computer restarts.
Real programs write files to:
- Save a userβs progress or settings for next time the app opens.
- Export results that someone can open in Excel, a text editor, or another program.
- Append events to a log file so you can see later what happened and when. (A log file is a plain text file with one line per event, usually with a timestamp.)
- Hand data to another program, since a file is a simple way to share.
Writing can fail, so the file-writing methods throw a checked exception called IOException:
- The disk could be full, or the folder might not exist.
- The file might be locked, or you may not have permission.
- A checked exception is one the compiler forces you to handle, by catching it or passing it on.
- You cannot ignore it and still compile.
βοΈ The simplest writer: FileWriter
The most direct tool for writing text is FileWriter:
- You give it a file name, then call
writeto put text in. - If the file does not exist,
FileWritercreates it for you. - If it does exist, by default it erases the old content first (more on that danger soon).
Here is the smallest possible example that writes one line of text.
import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { try (FileWriter writer = new FileWriter("greeting.txt")) { writer.write("Hello, Alex!"); // β
writes text into the file System.out.println("Done writing."); } catch (IOException e) { System.out.println("Could not write the file: " + e.getMessage()); } }}What each part does:
new FileWriter("greeting.txt")opens the file, creating it if needed.writer.write("Hello, Alex!")sends the text into the file.- The
try (...)is try-with-resources. It closes the file when the block finishes. - The
catchruns only if something goes wrong, like a disk problem.
Use FileWriter for a small amount of text and the least code. The downside is speed. Each write can go straight to the disk, and disk trips are slow. For lots of text in small pieces, BufferedWriter is faster.
Output
Done writing.π Writing efficiently with BufferedWriter
BufferedWriter is a wrapper you put around a FileWriter:
- The word buffer means a small holding area in memory.
- It collects your text in the buffer instead of sending each piece straight to the disk.
- It sends the text to the disk in bigger chunks.
- Fewer disk trips means faster writing.
Picture it: a FileWriter alone carries water one cup at a time. A BufferedWriter fills a bucket first, then pours the whole bucket.
This example writes two lines using a buffered writer.
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { writer.write("Hello, file!"); writer.newLine(); // β
moves to the next line writer.write("Second line."); System.out.println("Done writing."); } catch (IOException e) { System.out.println("Could not write the file: " + e.getMessage()); } }}Reading the code top to bottom:
new FileWriter("output.txt")is the real connection to the file.new BufferedWriter(...)wraps it to add the speed buffer on top.writer.write(...)adds text, the same as before.writer.newLine()adds the correct line break for your operating system.- Windows and Linux use different βend of lineβ characters. Typing
"\n"yourself may look wrong on Windows, butnewLine()always picks the right one.
Output
Done writing.The console only prints Done writing., but output.txt now holds the two lines you wrote.
π¨οΈ PrintWriter for convenient formatting
A third writer worth knowing is PrintWriter:
- It gives you the same
printlnandprintfyou already use withSystem.out. printlnadds the line break for you, so you skipnewLine().printfformats numbers and text with placeholders.
This example writes a small report using println and printf.
import java.io.PrintWriter;import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { try (PrintWriter writer = new PrintWriter(new FileWriter("report.txt"))) { writer.println("Daily Report"); // β
println adds the line break writer.println("------------"); writer.printf("Name: %s%n", "Riya"); // %s is text, %n is a new line writer.printf("Score: %d%n", 95); // %d is a whole number System.out.println("Report written."); } catch (IOException e) { System.out.println("Could not write: " + e.getMessage()); } }}What the placeholders mean:
%sis replaced by text, here the nameRiya.%dis replaced by a whole number, here95.%nis a line break, the file-friendly cousin ofnewLine().
Pick PrintWriter for neat, formatted output and the println/printf style. Pick plain BufferedWriter when you push raw text and want full control over line breaks.
Output
Report written.π The modern way: Files.write and Files.writeString
Since Java 7, the Files helper class makes writing a one-liner:
- It is part of NIO, the newer file tools in Java. NIO stands for New Input/Output.
- You do not create a writer, wrap it, or close it yourself.
- You just hand over a path and the content.
This example writes a list of lines in a single call.
import java.nio.file.Files;import java.nio.file.Paths;import java.io.IOException;import java.util.Arrays;import java.util.List;
public class Main { public static void main(String[] args) { List<String> lines = Arrays.asList("First line", "Second line", "Third line"); try { Files.write(Paths.get("output.txt"), lines); // β
writes all lines at once System.out.println("File written."); } catch (IOException e) { System.out.println("Could not write: " + e.getMessage()); } }}What that one important line does:
Paths.get("output.txt")turns the file name into aPathobject.Files.write(path, lines)writes every item in the list, each on its own line.- It opens, writes, and closes the file for you in that single call.
If you have just one String instead of a list, use Files.writeString (added in Java 11). It writes the whole String in one go.
import java.nio.file.Files;import java.nio.file.Paths;import java.io.IOException;
public class Main { public static void main(String[] args) { try { Files.writeString(Paths.get("note.txt"), "A short note saved in one line."); System.out.println("Note saved."); } catch (IOException e) { System.out.println("Could not write: " + e.getMessage()); } }}Reach for these Files methods when your content is ready, either as a list of lines or a single String, and you just want to drop it into a file. They throw IOException, so you still handle that.
Output
File written.β Appending instead of overwriting
This is the part beginners most often get wrong, so read it slowly. There are two modes when you open a file for writing:
- Overwrite mode wipes the file clean and starts fresh. This is the default.
- Append mode keeps the existing content and adds your new text at the end.
- The default overwrite is the trap. Write to an important file the normal way and the old content is gone, with no undo.
- Imagine a log file holding a week of events. Open it the default way, write one line, and you deleted a week of history.
To switch on append mode with FileWriter, pass true as the second argument.
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { // the true here means "append", not "overwrite" try (BufferedWriter writer = new BufferedWriter(new FileWriter("log.txt", true))) { writer.write("New log entry"); // β
added to the end, old lines kept writer.newLine(); System.out.println("Entry appended."); } catch (IOException e) { System.out.println("Could not append: " + e.getMessage()); } }}The whole difference is that small true:
- With it, each run adds a line to the end.
- Without it, each run erases everything that was there.
- For logs, where every event should be kept, append is what you want.
The Files class does the same with an option called StandardOpenOption.APPEND. You pass it after the content.
import java.nio.file.Files;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.io.IOException;import java.util.Arrays;import java.util.List;
public class Main { public static void main(String[] args) { List<String> moreLines = Arrays.asList("Another entry"); try { Files.write(Paths.get("log.txt"), moreLines, StandardOpenOption.APPEND); System.out.println("Appended with Files."); } catch (IOException e) { System.out.println("Could not append: " + e.getMessage()); } }}A few notes on StandardOpenOption.APPEND:
- The file must already exist, or you get an error.
- To create it when missing and append when present, add
StandardOpenOption.CREATEalongsideAPPEND. - Plain
Files.writewithout options always creates or overwrites, so you only add these options to append.
Output
Entry appended.π§ Flushing and why closing matters
Remember the buffer, the holding area in memory? It causes one surprise:
- When you call
write, your text often goes into the buffer first, not the disk. - The text sits there until the buffer fills up or you send it down on purpose.
- If your program ends or crashes first, the buffered text is lost.
- The file may end up empty or missing your last lines.
Sending the buffered text to the disk is called flushing. There are two ways it happens:
- You call
writer.flush()yourself to force everything out now. - You close the writer, and closing flushes automatically as its last act.
So closing is not just cleanup. It guarantees your data reaches the disk. The clean way to always close is try-with-resources, which you have seen in every example. Java closes the writer when the block ends, even on error. Compare the two styles below.
// β Risky: you must remember to close, and an error may skip itBufferedWriter writer = new BufferedWriter(new FileWriter("data.txt"));writer.write("important data");// if an exception happens here, close() never runs and data may be lostwriter.close();
// β
Safe: try-with-resources always closes (and flushes) for youtry (BufferedWriter w = new BufferedWriter(new FileWriter("data.txt"))) { w.write("important data");} // w is closed and flushed here, even if an error happenedSo always open writers with try-with-resources. You get the flush and the close for free, and your data is safe.
π§ͺ A worked example: overwrite, then append
Letβs tie it together with one realistic run. First we overwrite a fresh file with three lines. Then a separate program appends one line. Watching the file before and after makes the two modes clear.
This first program writes three lines, replacing any old content.
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("scores.txt"))) { writer.write("Alex scored 80"); writer.newLine(); writer.write("Riya scored 95"); writer.newLine(); writer.write("Sam scored 73"); writer.newLine(); System.out.println("Three lines written (overwrite)."); } catch (IOException e) { System.out.println("Write failed: " + e.getMessage()); } }}After this runs, scores.txt holds exactly three lines.
Output
Alex scored 80Riya scored 95Sam scored 73Now a second program appends one more line, keeping the three above. Notice the true for append mode.
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;
public class Main { public static void main(String[] args) { try (BufferedWriter writer = new BufferedWriter(new FileWriter("scores.txt", true))) { writer.write("Maya scored 88"); // β
added to the end, others kept writer.newLine(); System.out.println("One line appended."); } catch (IOException e) { System.out.println("Append failed: " + e.getMessage()); } }}After the append, the file has all four lines. The first three stayed because we used append mode.
Output
Alex scored 80Riya scored 95Sam scored 73Maya scored 88Leave out the true in the second program and the file would hold only Maya scored 88. That single argument is the line between adding to your data and destroying it.
β οΈ Common Mistakes
A few file-writing slip-ups to watch for.
- Accidentally overwriting a file. Opening for writing erases the file by default, so you can wipe out data without meaning to.
new FileWriter("log.txt"); // β erases the whole lognew FileWriter("log.txt", true); // β
keeps the log, adds to the end- Not closing the writer, so data is lost. Text can stay stuck in the buffer and never reach the disk.
BufferedWriter w = new BufferedWriter(new FileWriter("data.txt"));w.write("hello"); // β never closed, "hello" may be lost
try (BufferedWriter w = new BufferedWriter(new FileWriter("data.txt"))) { w.write("hello"); // β
closed and flushed automatically}- Ignoring IOException. Writing is a checked exception, so the program will not compile if you pretend it cannot fail.
Files.write(Paths.get("a.txt"), lines); // β does not compile, IOException unhandled
try { Files.write(Paths.get("a.txt"), lines); // β
handled} catch (IOException e) { System.out.println("Write failed: " + e.getMessage());}β Best Practices
Habits for writing files.
- Use try-with-resources every time. It closes the writer and flushes the buffer, so your data is safely saved even when an error happens.
- Choose append vs overwrite on purpose. Use append (
trueorStandardOpenOption.APPEND) for logs and growing files. Use overwrite only when you truly mean to replace the content. - Wrap a FileWriter in a BufferedWriter for lots of text. The buffer makes many small writes much faster.
- Reach for Files.write or Files.writeString for simple dumps. When you already have a list of lines or a single String, this is the shortest, clearest code.
- Always handle IOException. Report failures with a clear message instead of letting the program crash silently.
π§© What Youβve Learned
Nicely done. Letβs recap writing files.
- β Writing files makes data permanent on disk, surviving after the program ends.
- β
FileWriteris the simplest writer;BufferedWriterwraps it for speed and gives younewLine(). - β
PrintWriteradds friendlyprintlnandprintfformatting. - β
Files.writewrites a list of lines andFiles.writeStringwrites a String, each in one call. - β
Writing overwrites by default; use append mode (
trueorStandardOpenOption.APPEND) to keep existing content. - β
Closing the writer flushes the buffer, so use try-with-resources and always handle
IOException.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What happens to a file's existing content when you open it for writing by default?
Why: By default, opening a file for writing erases its contents; use append mode to keep them.
- 2
How do you append to a file with FileWriter instead of overwriting?
Why: new FileWriter(name, true) opens the file in append mode, keeping existing content.
- 3
What does Files.write do?
Why: Files.write saves the given content to a file in a single call and handles closing.
- 4
Why use try-with-resources when writing?
Why: try-with-resources closes and flushes the writer so all data is written, even on error.
π Whatβs Next?
You have used the classic streams and the Files helper. Next, letβs look at a reader built for efficient, line-by-line reading: BufferedReader. It pairs naturally with the writers you just learned.