Skip to content
Java io 5 min read

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:

FieldDescription
System.outStandard output — usually the terminal
System.errStandard 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: PrintStream is a byte stream (it extends OutputStream), 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() 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:

SpecifierMeaningExample
%sString"hello"
%dInteger42
%fFloating-point3.140000
%.2fFloat, 2 decimals3.14
%nPlatform newline
%bBooleantrue
%cCharacterA
%xHexadecimal2a

Tip: Always use %n instead 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 PrintStream in a loop and never call checkError(), 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.out when redirecting so that subsequent code is not surprised. Prefer a logging framework like SLF4J or java.util.logging for 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):

  1. The string is encoded to bytes using the configured charset (or platform default before Java 10).
  2. The encoded bytes are written to the internal buffer.
  3. If auto-flush is on, the buffer is flushed to the underlying OutputStream immediately.

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:

FeaturePrintStreamPrintWriter
Stream typeByte (OutputStream)Character (Writer)
Charset handlingPlatform default (pre-Java 10) / explicit (Java 10+)Explicit via OutputStreamWriter
Auto-flush triggerprintln() or newline byteprintln() only
Primary useSystem.out, System.err, legacy codePreferred for text file output
  • 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 PrintStream writes it
  • Java I/O — overview of the entire Java I/O framework
Last updated June 13, 2026
Was this helpful?