PrintStream
PrintStream is one of the most quietly familiar classes in all of Java — every time you call System.out.println(), you are using it. It wraps any byte output stream and adds convenient print(), println(), and printf() methods that let you write formatted text without ever throwing a checked IOException.
What Is PrintStream?
PrintStream lives in java.io and extends FilterOutputStream, which itself extends OutputStream. Unlike most I/O classes, it never throws IOException from its print or write methods. Instead, it silently sets an internal error flag that you can check with checkError(). This design made it very convenient for quick console output, though it also means errors can go unnoticed if you don’t check.
The two most well-known PrintStream instances you already have are:
| Field | Description |
|---|---|
System.out | Standard output — usually the terminal |
System.err | Standard error — also the terminal, but unbuffered by default |
You can also create your own PrintStream to wrap a file, a network socket, or any OutputStream.
Note:
PrintStreamis a byte stream (it extendsOutputStream), not a character stream. It converts characters to bytes using a charset — by default the platform’s default charset. For portable text file writing, prefer PrintWriter, which is a proper character stream.
Constructors
PrintStream offers several constructors so you can wrap different targets:
// Wrap an existing OutputStream
PrintStream ps1 = new PrintStream(outputStream);
// Wrap an OutputStream with auto-flush enabled
PrintStream ps2 = new PrintStream(outputStream, true);
// Wrap an OutputStream with a specific charset
PrintStream ps3 = new PrintStream(outputStream, true, "UTF-8");
// Write directly to a file by name (Java 1.5+)
PrintStream ps4 = new PrintStream("output.txt");
// Write to a File object with a specific charset
PrintStream ps5 = new PrintStream(new File("output.txt"), "UTF-8");
The auto-flush flag (second parameter) automatically flushes the buffer after each println() call or after a newline byte is written. System.out has auto-flush enabled.
Core Methods
print() and println()
print() writes its argument without a trailing newline. println() writes the argument followed by the platform’s line separator (\n on Unix, \r\n on Windows).
Both are overloaded to accept every primitive type plus String, char[], and Object. For objects, they call String.valueOf(obj), which in turn calls toString() — so your custom toString() methods work automatically.
PrintStream ps = System.out;
ps.print("Hello");
ps.println(", World!"); // Hello, World!
ps.println(42); // 42
ps.println(3.14); // 3.14
ps.println(true); // true
ps.println((Object) null); // null
Output:
Hello, World!
42
3.14
true
null
printf() and format()
printf() is an alias for format(). Both use java.util.Formatter internally and accept a format string plus varargs.
String name = "Alice";
int age = 30;
double salary = 75_000.50;
System.out.printf("Name: %-10s Age: %3d Salary: %,.2f%n", name, age, salary);
Output:
Name: Alice Age: 30 Salary: 75,000.50
Common format specifiers:
| Specifier | Meaning | Example |
|---|---|---|
%s | String | "hello" |
%d | Integer | 42 |
%f | Floating-point | 3.140000 |
%.2f | Float, 2 decimals | 3.14 |
%n | Platform newline | — |
%b | Boolean | true |
%c | Character | A |
%x | Hexadecimal | 2a |
Tip: Always use
%ninstead of"\n"inside format strings. It writes the correct line separator for the current platform.
append()
PrintStream also implements Appendable, so you can chain append() calls:
System.out.append("Java ").append("is ").append("fun!\n");
Output:
Java is fun!
checkError()
Because PrintStream swallows IOException, you should call checkError() if you need to know whether an error occurred:
PrintStream ps = new PrintStream(new FileOutputStream("out.txt"));
ps.println("Writing some text");
if (ps.checkError()) {
System.err.println("An error occurred while writing!");
}
Warning: If you write to a
PrintStreamin a loop and never callcheckError(), disk-full or permission errors will be silently ignored. Always check after critical writes.
Writing to a File
You can redirect output to a file by creating your own PrintStream:
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamToFile {
public static void main(String[] args) throws Exception {
try (PrintStream ps = new PrintStream(new FileOutputStream("report.txt"))) {
ps.println("=== Sales Report ===");
ps.printf("%-15s %10s%n", "Product", "Revenue");
ps.printf("%-15s %10.2f%n", "Widget A", 12345.67);
ps.printf("%-15s %10.2f%n", "Widget B", 9876.54);
}
System.out.println("Report written.");
}
}
Output (report.txt):
=== Sales Report ===
Product Revenue
Widget A 12345.67
Widget B 9876.54
Redirecting System.out
A classic debugging technique is to redirect System.out to a file for the duration of a run:
import java.io.FileOutputStream;
import java.io.PrintStream;
public class RedirectDemo {
public static void main(String[] args) throws Exception {
PrintStream originalOut = System.out;
try (PrintStream fileOut = new PrintStream(new FileOutputStream("log.txt"))) {
System.setOut(fileOut);
System.out.println("This goes to log.txt, not the console.");
}
System.setOut(originalOut);
System.out.println("Back to the console.");
}
}
Tip: Always save and restore
System.outwhen redirecting so that subsequent code is not surprised. Prefer a logging framework like SLF4J orjava.util.loggingfor production code — they are more flexible and thread-safe.
Charset Awareness (Java 10+)
Before Java 10, PrintStream used the platform default charset and provided no easy way to specify another. Java 10 added charset-aware constructors:
// Java 10+: explicit UTF-8, no relying on platform default
PrintStream ps = new PrintStream(new FileOutputStream("utf8.txt"), true, java.nio.charset.StandardCharsets.UTF_8);
ps.println("Ünïcödé tëxt!");
ps.close();
If you are writing code that will run across different operating systems, always specify the charset explicitly to avoid garbled output.
Under the Hood
PrintStream wraps the provided OutputStream in a BufferedWriter (internally) for its text-writing methods, and passes through byte-level calls directly. When you call println(String):
- The string is encoded to bytes using the configured charset (or platform default before Java 10).
- The encoded bytes are written to the internal buffer.
- If auto-flush is on, the buffer is flushed to the underlying
OutputStreamimmediately.
Because all print methods catch any IOException internally and set a boolean error flag, there are no checked exceptions to handle — which is why System.out.println() doesn’t require a try-catch. The tradeoff is that errors are invisible unless you actively call checkError().
The key distinction from PrintWriter:
| Feature | PrintStream | PrintWriter |
|---|---|---|
| Stream type | Byte (OutputStream) | Character (Writer) |
| Charset handling | Platform default (pre-Java 10) / explicit (Java 10+) | Explicit via OutputStreamWriter |
| Auto-flush trigger | println() or newline byte | println() only |
| Primary use | System.out, System.err, legacy code | Preferred for text file output |
Related Topics
- PrintWriter — the character-stream sibling of
PrintStream, preferred for writing text files portably - BufferedWriter — buffered character stream writing with
newLine()support - FileOutputStream — the underlying byte stream you often wrap with
PrintStream - Byte vs Character Streams — understand when to use each type of stream
- Scanner — the natural complement for reading formatted input the way
PrintStreamwrites it - Java I/O — overview of the entire Java I/O framework