PrintWriter
PrintWriter is the go-to class when you want convenient, formatted text output to a file, a network connection, or any Writer. It gives you the familiar println() and printf() methods you already know from System.out — but targeted at character streams instead of the console.
What Is PrintWriter?
PrintWriter lives in java.io and extends Writer. It wraps any existing Writer (or OutputStream) and adds a rich set of print methods that handle all Java primitive types, objects, and formatted strings. Unlike PrintStream, PrintWriter works at the character level and supports specifying a character encoding, making it portable and Unicode-friendly.
Key characteristics:
- No checked
IOException— methods likeprint()andprintln()never throwIOException. Instead, errors set an internal error flag you can check withcheckError(). - Optional auto-flush — you can configure it to flush automatically after every
println(),printf(), orformat()call. - Encoding support — you can specify a
Charsetso your output is always UTF-8 (or whatever you need), regardless of platform defaults.
Tip: If you just need console output,
System.out(aPrintStream) is fine. Reach forPrintWriterwhen writing to files, network sockets, or any otherWriter-based destination.
Constructors
PrintWriter has several constructors for different use cases:
| Constructor | Description |
|---|---|
PrintWriter(Writer out) | Wraps an existing Writer, no auto-flush |
PrintWriter(Writer out, boolean autoFlush) | Wraps a Writer; flushes after println/printf/format if true |
PrintWriter(OutputStream out) | Wraps an OutputStream; uses platform default encoding |
PrintWriter(OutputStream out, boolean autoFlush) | Same with auto-flush option |
PrintWriter(String fileName) | Creates a file by name; uses platform default encoding |
PrintWriter(String fileName, Charset charset) | Creates a file by name with explicit encoding (Java 10+) |
PrintWriter(File file) | Creates a named File; uses platform default encoding |
PrintWriter(File file, Charset charset) | Creates a File with explicit encoding (Java 10+) |
Note: The constructors that accept a file name or
Fileobject open the file for writing (truncating any existing content). They are a shortcut — internally, Java wraps the file in aBufferedWriterfor you.
Basic Usage
import java.io.PrintWriter;
import java.io.IOException;
public class BasicPrintWriter {
public static void main(String[] args) throws IOException {
try (PrintWriter pw = new PrintWriter("output.txt")) {
pw.println("Hello, PrintWriter!");
pw.println("Writing to a file is easy.");
pw.printf("Pi is approximately %.5f%n", Math.PI);
}
System.out.println("File written successfully.");
}
}
Output:
File written successfully.
The resulting output.txt contains:
Hello, PrintWriter!
Writing to a file is easy.
Pi is approximately 3.14159
The try-with-resources block calls close() automatically, which flushes the internal buffer and closes the underlying stream.
Specifying a Character Encoding
Always specify the charset explicitly to avoid platform-dependent encoding surprises:
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class EncodedPrintWriter {
public static void main(String[] args) throws IOException {
try (PrintWriter pw = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("utf8_out.txt"),
StandardCharsets.UTF_8)))) {
pw.println("Héllo Wörld — Unicode works great!");
pw.printf("Amount: €%.2f%n", 1234.56);
}
System.out.println("UTF-8 file written.");
}
}
Output:
UTF-8 file written.
Tip: In Java 10+, you can use the simpler
new PrintWriter("file.txt", StandardCharsets.UTF_8)constructor instead of stacking streams manually.
println(), print(), and printf()
PrintWriter overloads its print methods for every type:
import java.io.PrintWriter;
import java.io.IOException;
public class PrintMethods {
public static void main(String[] args) throws IOException {
try (PrintWriter pw = new PrintWriter("types.txt")) {
pw.println(42); // int
pw.println(3.14); // double
pw.println(true); // boolean
pw.println('A'); // char
pw.println("Java"); // String
pw.println(new int[]{1,2,3}); // Object (prints reference, not content)
pw.print("no newline here");
pw.println(" — newline now");
pw.printf("Formatted: %s scored %d/%d%n", "Alice", 95, 100);
pw.format("Same thing: %s scored %d/%d%n", "Bob", 88, 100);
}
}
}
Note:
println(Object)callsString.valueOf(obj)internally, which callstoString(). For arrays, that prints something like[I@1a2b3c(the reference), not the contents. UseArrays.toString()to get readable array output.
Auto-Flush
When auto-flush is enabled, PrintWriter flushes the underlying stream after every println(), printf(), or format() call. This is useful when writing to a socket or log stream where readers need data immediately:
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
public class AutoFlushDemo {
public static void main(String[] args) {
// auto-flush = true: each println() flushes immediately
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(System.out, StandardCharsets.UTF_8), true);
pw.println("This line is flushed immediately.");
pw.println("So is this one.");
pw.close();
}
}
Output:
This line is flushed immediately.
So is this one.
Warning: Auto-flush only triggers on
println(),printf(), andformat()— NOT on plainprint()orwrite(). If you rely on auto-flush, make sure you use those specific methods.
Error Handling with checkError()
PrintWriter swallows IOExceptions silently and sets an internal error flag instead. Call checkError() to check whether any write has failed:
import java.io.PrintWriter;
import java.io.IOException;
public class ErrorCheck {
public static void main(String[] args) throws IOException {
try (PrintWriter pw = new PrintWriter("status.txt")) {
pw.println("Writing line 1");
pw.println("Writing line 2");
if (pw.checkError()) {
System.err.println("An I/O error occurred during writing!");
} else {
System.out.println("Write succeeded.");
}
}
}
}
Output:
Write succeeded.
Warning: Once the error flag is set, it stays set even if subsequent writes succeed. Check
checkError()after the critical writes, not just at the very end.
Writing to a Network Socket
PrintWriter is commonly used for socket-based communication because it layers cleanly over any OutputStream:
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
public class SocketWrite {
public static void send(String host, int port, String message) throws IOException {
try (Socket socket = new Socket(host, port);
PrintWriter pw = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(),
StandardCharsets.UTF_8), true)) {
pw.println(message); // auto-flush ensures server receives it immediately
}
}
}
The auto-flush here is important: the server needs to read the line before the client closes the connection. See Socket Programming for a full client/server example.
Stacking with BufferedWriter for Performance
When writing large amounts of text to a file, wrap a BufferedWriter between PrintWriter and the underlying stream to batch writes and reduce system calls:
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedPrintWriter {
public static void main(String[] args) throws IOException {
// Outer PrintWriter gives us println/printf convenience
// Inner BufferedWriter reduces system calls with a 8 KB buffer
try (PrintWriter pw = new PrintWriter(
new BufferedWriter(new FileWriter("report.txt")))) {
for (int i = 1; i <= 10_000; i++) {
pw.printf("Row %d: value=%.4f%n", i, Math.random());
}
}
System.out.println("Report written.");
}
}
Output:
Report written.
Tip: The convenience constructors
new PrintWriter("filename")add aBufferedWriterinternally, so for simple file writes you don’t need to stack them yourself. Manual stacking is needed when you want a specific charset or a custom buffer size.
PrintWriter vs PrintStream
Both provide print(), println(), and printf(), but they differ in important ways:
| Feature | PrintStream | PrintWriter |
|---|---|---|
| Extends | FilterOutputStream (byte stream) | Writer (character stream) |
| Encoding | Platform default (limited control) | Full Charset support |
| Use case | System.out, System.err | Files, sockets, any Writer |
| Checked exceptions | None thrown | None thrown |
checkError() | Yes | Yes |
For modern file and network output, prefer PrintWriter. PrintStream is mostly there for legacy compatibility and console output.
Under the Hood
Internally, PrintWriter delegates every print* and write* call to its wrapped Writer. The class keeps a boolean trouble field — the error flag set by checkError(). There is no internal buffer of its own; buffering is the responsibility of whatever Writer sits underneath (which is why stacking a BufferedWriter matters for performance).
The auto-flush mechanism is implemented in println(), printf(), and format() — after writing the content, they call out.flush() if the autoFlush flag is true. Plain write() and print() calls never flush regardless of the flag.
printf() and format() are identical — they both call new Formatter(this, locale).format(fmt, args) under the hood. The Locale-aware overloads (printf(Locale, String, Object...)) let you control decimal separators and number formatting for international output.
Thread safety: PrintWriter synchronizes on the wrapped Writer object (its lock). Method calls are individually thread-safe — one thread won’t corrupt another’s characters. However, interleaved println() calls from multiple threads can still produce mixed-up lines. For coordinated multi-threaded logging, use a dedicated logging framework.
Key Methods at a Glance
| Method | Description |
|---|---|
print(T value) | Print any type without a newline |
println(T value) | Print any type followed by a newline |
printf(String fmt, Object... args) | Formatted output (locale-sensitive overload available) |
format(String fmt, Object... args) | Identical to printf() |
write(int c) / write(String s) | Low-level character write (no auto-flush) |
flush() | Flush the underlying stream |
close() | Flush and close the stream |
checkError() | Returns true if any write failed |
Related Topics
- BufferedWriter — efficient buffered writing; stack it under
PrintWriterfor high-throughput file output - PrintStream — the byte-stream sibling powering
System.outandSystem.err - FileWriter — simple unbuffered character writer for basic file output
- InputStreamReader / OutputStreamWriter — bridge streams for full charset control
- Socket Programming — real-world use of
PrintWriterover a network socket - Byte vs Character Streams — understand when to choose
WriteroverOutputStream