W3docs

Java PrintWriter

Write formatted text to streams in Java with the PrintWriter class — print, println, printf, format.

The character-streams chapter introduced Writer and its core method, write(String). That's enough for anything, but it isn't ergonomic — printing a number means w.write(Integer.toString(n)), printing a line means remembering to append the line terminator yourself, and formatted output requires String.format on every call. PrintWriter is the decorator that fixes the ergonomics: it adds print, println, printf, and format on top of any underlying Writer or OutputStream.

It's the file-side counterpart to the System.out you've been using since the first chapter — same API surface, written to a file instead of the console.

What PrintWriter adds

void  print(boolean | char | int | long | float | double | String | Object);
void  println(...);                                  // same overloads, plus the line terminator
PrintWriter printf(String format, Object... args);   // String.format under the hood
PrintWriter format(String format, Object... args);   // alias for printf
PrintWriter append(CharSequence s);                  // returns this (for chaining)

Plus the inherited Writer methods (write, flush, close). The point is the typed overloads: you can write any primitive or object directly and PrintWriter calls String.valueOf for you.

try (PrintWriter w = new PrintWriter(Files.newBufferedWriter(path))) {
  w.println("header");
  w.printf("count = %d%n", 42);
  w.printf("rate  = %.2f%%%n", 0.875 * 100);
  w.println();                                       // blank line
}

The try-with-resources close() flushes the buffer; without it the tail-buffer trap from the buffered-streams chapter applies just as much to PrintWriter.

Constructors

The useful ones, in order of preference:

PrintWriter(Path file, Charset charset);             // Java 10+, opens the file with the charset
PrintWriter(Writer out);                             // wrap any Writer (typical: a BufferedWriter)
PrintWriter(Writer out, boolean autoFlush);          // same, with autoFlush on println/printf
PrintWriter(OutputStream out, boolean autoFlush, Charset charset);

The Path + Charset constructor is the simplest one for "open this file and write to it":

try (PrintWriter w = new PrintWriter(path.toFile(), StandardCharsets.UTF_8)) {
  w.println("hello");
}

It opens the file, wraps it in an OutputStreamWriter with the supplied charset, wraps that in a BufferedWriter, and gives you a PrintWriter. The four-layer stack you used to assemble by hand collapses to one line.

Always pass an explicit charset. The no-charset constructors — new PrintWriter("file.txt"), new PrintWriter(outputStream) — fall back to the JVM's default encoding, which is the same portability hazard described in the character-streams chapter. UTF-8 is the right default.

The swallowed IOException

PrintWriter differs from every other Writer in one important way: it does not throw IOException. None of print, println, printf, or write declare it. If an underlying I/O call fails, PrintWriter swallows the exception and sets an internal "error" flag.

This is convenient — you can write a long block of println calls without try/catch around each one — but it means a failed write is silent. You have to ask:

if (w.checkError()) {
  // something went wrong; the underlying IOException was swallowed
  throw new IOException("write to " + path + " failed");
}

checkError() is the only way to find out. There is no way to retrieve the original IOException — by the time you check, it's gone. So:

  • For console output (the System.out use case), the swallow is fine: nobody handles a failed write to a terminal.
  • For files where a partial write is a real problem (a logfile, a save file, a generated report), check checkError() at the end of the block, or use a BufferedWriter and call write yourself so the exception propagates.

Autoflush

The autoFlush constructor argument controls whether each println, printf, or format call calls flush() afterward. Default is off:

new PrintWriter(out, false)                          // explicit close/flush only
new PrintWriter(out, true)                           // flush after every println/printf/format

print and write never autoflush, even with the flag on — only the line-and-format methods do. That's why System.out.print("waiting...") may sit invisibly while the next computation runs, and System.out.println("waiting...") shows up immediately.

For files, leave autoflush off and let close() (via try-with-resources) handle it. For an interactive log you're tailing, turn it on or call flush() after each batch.

println and the platform line separator

println() writes System.lineSeparator() — \n on Unix and macOS, \r\n on Windows. Same for the %n specifier in printf. That's a feature for terminal output and a bug for data files; the discussion in the buffered-streams chapter (under BufferedWriter.newLine) applies here verbatim:

w.printf("row,%d%n", 1);                             // platform-dependent terminator
w.printf("row,%d\n", 1);                             // portable \n

When the output is going to be read by anything other than the local machine, write \n explicitly.

Comparison with PrintStream

PrintStream is the byte-oriented sibling of PrintWriter — same API, different ancestor. System.out and System.err are PrintStream instances. For file output, prefer PrintWriter: it forces you to think about character encoding (because the constructor takes a Charset), where PrintStream will silently use the default encoding for any non-ASCII character you print.

The next chapter, Java PrintStream, goes into the differences in detail.

A worked example: writing a tiny CSV file

The program below opens a temp file with a PrintWriter, writes a small CSV (header + rows), demonstrates printf formatting, calls checkError() to confirm the writes succeeded, and finally shows the swallow-the-exception behaviour by writing to a writer whose underlying stream has been closed.

java— editable, runs on the server

What to take from the run:

  • The CSV came out exactly as the printf format specified. %.2f rounded the price to two decimals; %-10s left-aligned within a 10-character column; \n (not %n) kept the line terminator portable. Format strings are the single biggest reason to reach for PrintWriter over plain Writer.
  • The first try-with-resources took the four-layer stack — Path → FileOutputStream → OutputStreamWriter(UTF-8) → BufferedWriter → PrintWriter — and collapsed it into one constructor call. The (File, Charset) constructor is the one you want for "open this file, write text to it, close it cleanly."
  • The checkError() check ran before the close. Once close() runs, the flag check is harder to act on — you've left the try block already. Inside the block is the place to check.
  • The autoflush-on PrintWriter wrapping System.out printed the report column-aligned because each printf ended in %n, which triggered the flush. It is not wrapped in a try-with-resources — closing a PrintWriter that decorates System.out would close System.out itself and silence everything printed afterward, so the example flushes instead.
  • The fourth block constructed a writer whose write always throws and pointed a PrintWriter at it. The println returned normally — no exception to catch. checkError() returned true, which was the only way to find out the write had failed. That's the trade-off of the swallow-and-flag design: convenient for casual code, dangerous if you don't check.

What's next

PrintWriter is the character-oriented file writer. Its sibling, Java PrintStream, is the byte-oriented one that powers System.out and System.err — same API, different ancestor, and the reason terminal output works for any character that fits in the platform's default charset.

Practice

Practice
You write a 10,000-line file with `PrintWriter` and never call `checkError()`. Three lines in the middle failed to write because of a disk-full condition. What does the resulting file look like, and what does the program report?
You write a 10,000-line file with `PrintWriter` and never call `checkError()`. Three lines in the middle failed to write because of a disk-full condition. What does the resulting file look like, and what does the program report?
Was this page helpful?