Java BufferedWriter

In the last lesson you learned about Java BufferedReader. Writing has the very same problem and the very same fix. When you save a lot of text, talking to the disk over and over is slow. BufferedWriter solves this by collecting your text in memory first, then sending it to the disk in larger pieces.

πŸ€” The problem BufferedWriter solves

Say your program wants to save a thousand lines:

  • The plain FileWriter can send each piece straight to the disk as you write it.
  • A disk is slow to talk to. Doing it a thousand times is like a thousand short trips to the store, buying one item each time.

A buffer fixes this:

  • The word buffer means a small holding area in memory.
  • BufferedWriter keeps your text there instead of running to the disk for every piece.
  • When it fills up, or when you close the writer, it sends everything in one bigger trip.
  • Fewer 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 at once.

Writing can fail, so the methods throw a checked exception called IOException:

  • The disk could be full, the folder might not exist, 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.

🧱 Wrapping a FileWriter

BufferedWriter does not connect to a file by itself. It wraps another writer that does, usually FileWriter. You build it in two layers: the FileWriter is the pipe to the disk, and the BufferedWriter is the speed buffer on top.

Here is the pattern you will use almost every time.

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("greeting.txt"))) {
writer.write("Hello, Alex!"); // βœ… goes into the buffer first
System.out.println("Done writing.");
} catch (IOException e) {
System.out.println("Could not write the file: " + e.getMessage());
}
}
}

Reading the code from the inside out:

  • new FileWriter("greeting.txt") opens the file, creating it if it does not exist.
  • new BufferedWriter(...) wraps that FileWriter to add the speed buffer.
  • writer.write("Hello, Alex!") puts the text into the buffer.
  • The try (...) is try-with-resources, which closes the writer when the block ends.
  • Closing sends the buffer to the disk, so the file ends up with your text.

The console prints only one line, but the file now holds your greeting.

Output

Done writing.

And the file greeting.txt contains this.

greeting.txt

Hello, Alex!

✏️ write, newLine, and flush

The three main methods you will use all the time:

  • write(String) adds text to the buffer. No line break, so the next write continues on the same line.
  • newLine() adds the correct line break for your operating system.
  • flush() forces the buffer to the disk now, without closing the writer.

newLine() matters more than it looks. Windows and Linux use different β€œend of line” characters. Typing "\n" yourself may look wrong on Windows, but newLine() always picks the right one.

This example writes two lines using write and newLine.

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("notes.txt"))) {
writer.write("First line of notes.");
writer.newLine(); // βœ… move to the next line
writer.write("Second line of notes.");
writer.newLine();
System.out.println("Two lines written.");
} catch (IOException e) {
System.out.println("Could not write: " + e.getMessage());
}
}
}

Notice the order: write some text, newLine to end the line, then write the next. Without the newLine calls, both pieces would sit on one long line.

Output

Two lines written.

The file notes.txt now holds two separate lines.

notes.txt

First line of notes.
Second line of notes.

Call flush() yourself mainly in long-running programs that need each piece on disk immediately, like a live log. Short programs do not need it, because closing the writer flushes for you. More on this soon.

πŸ” Writing many lines in a loop

The buffer really shines when you write a lot of text. A loop is the natural way to write many lines, and BufferedWriter keeps it fast because the lines pile up in the buffer instead of each one running to the disk.

This example writes ten numbered lines.

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("lines.txt"))) {
for (int i = 1; i <= 10; i++) {
writer.write("Line number " + i); // βœ… each pass adds one line
writer.newLine();
}
System.out.println("Wrote 10 lines.");
} catch (IOException e) {
System.out.println("Could not write: " + e.getMessage());
}
}
}

Walking through the loop:

  • The counter i goes from 1 to 10.
  • Each pass builds a small line of text with the current number.
  • write adds that text to the buffer, then newLine ends the line.
  • All ten lines collect in the buffer and reach the disk together when the writer closes.

Output

Wrote 10 lines.

The file lines.txt ends up with all ten lines.

lines.txt

Line number 1
Line number 2
Line number 3
Line number 4
Line number 5
Line number 6
Line number 7
Line number 8
Line number 9
Line number 10

This is where the speed difference shows. A plain FileWriter could touch the disk on each write. The buffer makes them travel together in fewer trips, and the more lines you write, the bigger the win.

βž• Appending instead of overwriting

Here 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, pass true as the second argument to FileWriter. The BufferedWriter around it does not change.

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.

Output

Entry appended.

If log.txt already held two lines and you run this once, the file now has three.

log.txt

Server started
User logged in
New log entry

πŸ’§ The buffer gotcha: flush and close

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.
  • It 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 it
BufferedWriter writer = new BufferedWriter(new FileWriter("data.txt"));
writer.write("important data");
// if an exception happens here, close() never runs and the buffer is lost
writer.close();
// βœ… Safe: try-with-resources always closes (and flushes) for you
try (BufferedWriter w = new BufferedWriter(new FileWriter("data.txt"))) {
w.write("important data");
} // w is closed and flushed here, even if an error happened

So always open a BufferedWriter with try-with-resources. You get the flush and the close for free, and your data is safe.

This tiny program shows the danger of skipping the close. It writes text but never closes the writer, so the buffer may never reach the disk.

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("risky.txt"));
writer.write("This might never be saved."); // ❌ sits in the buffer
// no close(), no flush() β€” the program just ends here
System.out.println("Program finished.");
}
}

The console says the program finished, yet risky.txt can come out empty because the text never left the buffer.

Output

Program finished.

The buffer can swallow your data

If text stays in the buffer and the writer is never flushed or closed, that text never reaches the file. Always close the writer, and the easiest way is try-with-resources.

πŸ§ͺ 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.

scores.txt

Alex scored 80
Riya scored 95
Sam scored 73

Now 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.

scores.txt

Alex scored 80
Riya scored 95
Sam scored 73
Maya scored 88

Leave 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 BufferedWriter slip-ups to watch for.

  • 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
}
  • Overwriting instead of appending. Opening for writing erases the file by default, so you can wipe out data without meaning to.
new BufferedWriter(new FileWriter("log.txt")); // ❌ erases the whole log
new BufferedWriter(new FileWriter("log.txt", true)); // βœ… keeps the log, adds to the end
  • Forgetting newLine, so everything lands on one line. write does not add a line break for you.
writer.write("First");
writer.write("Second"); // ❌ file reads "FirstSecond" on one line
writer.write("First");
writer.newLine(); // βœ… now they sit on separate lines
writer.write("Second");

βœ… Best Practices

Habits for writing with BufferedWriter.

  • 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.
  • Wrap a FileWriter, not the other way around. The pattern is new BufferedWriter(new FileWriter(path)), with the buffer on the outside.
  • Choose append vs overwrite on purpose. Pass true to FileWriter for logs and growing files. Use the default overwrite only when you truly mean to replace the content.
  • Use newLine for line breaks. It writes the correct end-of-line for the system, so your file looks right on Windows and Linux alike.
  • Call flush only when you need it. For long-running programs that must save as they go, flush() pushes the buffer out now. Short programs can rely on close.
  • 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 BufferedWriter.

  • βœ… BufferedWriter wraps another writer (usually FileWriter) and writes text faster by buffering it in memory.
  • βœ… The pattern is new BufferedWriter(new FileWriter(path)).
  • βœ… Use write(String) to add text, newLine() for a system-correct line break, and flush() to push the buffer out now.
  • βœ… Writing in a loop is fast because lines collect in the buffer and travel in fewer disk trips.
  • βœ… Writing overwrites by default; pass true to FileWriter for append mode that keeps existing content.
  • βœ… Data can stay in the buffer until flush or close, so always use try-with-resources and handle IOException.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does BufferedWriter wrap to write to a file?

    Why: BufferedWriter wraps another writer, usually a FileWriter, and adds a memory buffer for speed.

  2. 2

    Why can text be lost if you never close a BufferedWriter?

    Why: Writes go to the buffer first; without flush or close, that buffered text never reaches the file.

  3. 3

    How do you append with FileWriter instead of overwriting?

    Why: new FileWriter(path, true) opens the file in append mode, keeping existing content.

  4. 4

    What does writer.newLine() do?

    Why: newLine() writes the correct end-of-line character for the operating system you run on.

πŸš€ What’s Next?

You can now read and write files efficiently with buffered streams. Next, let’s step into a new area: running more than one task at the same time. Threads let your program do several things at once, like writing a file while still responding to the user.

Introduction to Threads in Java

Share & Connect