File Navigation and I/O ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-31

Java Standard Streams: System.in, System.out and System.err

A student writes int n = System.in.read();, types 5 on the keyboard and gets n = 53. The next call to read() returns 10, although nothing else was typed. There is no bug: System.in delivers bytes, not numbers. 53 is the code of the character '5', and 10 is the line feed from the Enter key. This lesson shows how the standard streams in Java work and how to use them correctly.

Standard streams in Java are three ready-made streams that the JVM creates at startup and stores in the fields of the java.lang.System class: System.in (standard input), System.out (standard output) and System.err (standard error). The java.lang package is imported automatically, so these streams are available in every program with no import and no object creation.

1. The System class and the three standard streams

The System class from java.lang keeps references to the three standard streams in the fields in, out and err, declared as public static final. They are bound to the standard file descriptors of the process, 0, 1 and 2, which the operating system sends to the terminal by default: input comes from the keyboard, output and errors go to the screen.

Besides the streams, System wraps other properties of the runtime: the current time (currentTimeMillis(), nanoTime()), system properties (getProperty()), environment variables (getenv()) and the line separator (lineSeparator()). The class has a private constructor and only static members, so you never create a System object.

Field Type OS descriptor Connected to by default Used for
System.in InputStream 0 (stdin) The keyboard in a terminal Input from the user
System.out PrintStream 1 (stdout) The terminal screen Normal program output
System.err PrintStream 2 (stderr) The terminal screen Error messages and diagnostics

System.in is a byte input stream (its basic methods are covered in Java Byte Streams); System.out and System.err are byte output streams with convenient printing methods. The general idea of a data stream and of wrapping streams is explained in Java I/O Streams.

2. System.out: printing to the console with PrintStream

System.out is an object of the PrintStream class connected to standard output. The call System.out.println("Hi") reads like this: take the static field out of the System class and call the method println() on that object; it prints the value and moves to a new line.

The methods of PrintStream turn values of any type into text and, unlike most streams, do not throw IOException: a write error only sets an internal flag.

Method What it does Edge case
print(x) Prints a value of any type without a line break A String variable holding null prints null; the literal print(null) does not compile (ambiguous: print(char[]) or print(String)); print((char[]) null) throws NullPointerException
println(x), println() Prints the value and the OS line separator (System.lineSeparator()) println(char[]) prints the characters of the array, while println("" + chars) prints [C@ and a hash
printf(format, args...), format(...) Formatted output from a template: %d, %s, %.2f, %n A wrong argument type throws IllegalFormatConversionException; the decimal separator depends on the locale (3,14 in German or French)
write(int b), write(byte[] buf, int off, int len) Writes raw bytes with no conversion write(int) uses only the low 8 bits
flush() Pushes the buffer to the destination Usually not needed for System.out: autoflush is on
checkError() Flushes the buffer and returns true if a write error occurred The only way to learn about a failure: the print methods throw no exceptions
public class PrintDemo {
    public static void main(String[] args) {
        String name = "Anna";
        int age = 25;
        double score = 4.567;

        System.out.print("Name: ");
        System.out.println(name);
        System.out.println("Age: " + age);
        System.out.printf("Score: %.1f, age next year: %d%n", score, age + 1);
    }
}
// Output (US locale):
// Name: Anna
// Age: 25
// Score: 4.6, age next year: 26

The JVM creates System.out with automatic flushing (autoflush), so the text appears in the console right after println() with no call to flush(). The flip side is that when you print hundreds of thousands of lines in a loop, every call goes to the OS and the output becomes the bottleneck (see the FAQ). The character-stream counterpart of PrintStream is PrintWriter; the differences are covered in Java Character Streams.

3. System.err: how it differs from System.out

System.err is the same kind of PrintStream with the same methods, but it writes to a separate OS channel, the standard error stream (descriptor 2). The split lets you separate errors from useful output: when System.out is redirected to a file, the messages from System.err still show up on the screen.

public class ErrDemo {
    public static void main(String[] args) {
        System.out.println("Processing started");
        System.err.println("Error: file data.csv not found");
        System.out.println("Processing finished");
    }
}

If you run the program with standard output redirected to a file, only the error line stays on the screen:

java ErrDemo > out.txt
# On screen:    Error: file data.csv not found
# In out.txt:   Processing started
#               Processing finished

java ErrDemo > out.txt 2> err.txt   # output and errors into different files
java ErrDemo > all.txt 2>&1         # both streams into one file

The no-argument e.printStackTrace() also writes to System.err, which is why stack traces are usually highlighted in red in an IDE. Real applications use a logger (java.util.logging, SLF4J with Logback) instead of printing to System.err directly; the logger decides where and in which format to write messages.

Why the lines come out in the wrong order

In an IDE console a red line from System.err sometimes appears before a System.out line that was printed earlier. This is not a Java bug: out and err are two independent OS channels, and the IDE reads them in parallel. If the order of messages matters, write them to the same stream.

4. System.in: reading from the keyboard

System.in is a byte input stream (type InputStream) connected to standard input. The method System.in.read() reads one byte and returns it as an int from 0 to 255, or -1 at the end of input. To get lines and numbers, wrap the stream in a BufferedReader or use Scanner.

The call read() blocks the program until the user presses Enter: the terminal passes the typed line as a whole, together with the end-of-line character.

Example 1. Reading byte by byte with System.in.read()

import java.io.IOException;

public class ReadBytes {
    public static void main(String[] args) throws IOException {
        System.out.print("Enter a digit: ");
        int first = System.in.read();
        int second = System.in.read();
        System.out.println("first = " + first + ", as a char: " + (char) first
                + ", as a number: " + (first - '0'));
        System.out.println("second = " + second);
    }
}
// Input: 5 and Enter
// Output (Linux, macOS):
// Enter a digit: 5
// first = 53, as a char: 5, as a number: 5
// second = 10
// On Windows second = 13: Enter produces the pair \r\n, and \r comes first

This is the case from the start of the lesson: 53 is the code of the character '5', and the "extra" byte is the line break that stayed in the stream and went to the second call. A non-ASCII letter in UTF-8 takes two or more bytes, so byte-by-byte reading is suitable only for the simplest cases.

Example 2. Reading lines and numbers with BufferedReader

To read lines, wrap System.in in an InputStreamReader (turns bytes into characters) and a BufferedReader (adds readLine()). How this bridge works and why the charset matters is explained in the section InputStreamReader and OutputStreamWriter.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ConsoleSum {
    public static void main(String[] args) throws IOException {
        BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("First number: ");
        int a = Integer.parseInt(console.readLine().trim());
        System.out.print("Second number: ");
        int b = Integer.parseInt(console.readLine().trim());
        System.out.println("Sum: " + (a + b));
        // we do not close console: closing it would also close System.in
    }
}
// Input: 7 and 35
// Output:
// First number: 7
// Second number: 35
// Sum: 42
// Input "abc": java.lang.NumberFormatException: For input string: "abc"
// Ctrl+D instead of input: readLine() returns null, and trim() throws NullPointerException

If you need to parse the input straight into numbers and words (nextInt(), nextDouble()), the Scanner class is more convenient. It is covered in Java User Input: Reading Data from Keyboard with System.in and Scanner.

How to end the input

End of standard input from the keyboard is signaled with Ctrl+D on Linux and macOS or Ctrl+Z followed by Enter on Windows. After that System.in.read() returns -1 and readLine() returns null. If the input is redirected from a file (java App < input.txt), the end of the file is the end of input.

5. Redirecting: setIn, setOut, setErr

You can replace the standard streams from inside the program with the static methods System.setIn(), System.setOut() and System.setErr(). After the call, all code that writes to System.out or reads from System.in works with the new stream: a file, an in-memory buffer or a network connection.

Method What it does Typical use
System.setIn(InputStream in) Replaces standard input Feed prepared input to code in a test: new ByteArrayInputStream("42\n".getBytes())
System.setOut(PrintStream out) Replaces standard output Capture the output of a method in a test, write output to a file
System.setErr(PrintStream err) Replaces the error stream Collect error messages and stack traces in a log

The fields in, out and err are declared final, so ordinary code cannot assign to them: System.out = ... does not compile. The setXxx() methods change them through native JVM code, a rare exception to the rule that a final field never changes.

Example 3. Capturing output into a string

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class CaptureOutput {
    public static void main(String[] args) {
        PrintStream original = System.out;              // keep it to restore later
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        System.setOut(new PrintStream(buffer, true));
        try {
            System.out.println("This line will not reach the console");
        } finally {
            System.setOut(original);                    // always restore
        }
        System.out.println("Captured: " + buffer.toString().trim());
    }
}
// Output:
// Captured: This line will not reach the console

This is how tests check methods that print their result instead of returning it. Restoring the stream in finally is mandatory: otherwise all the following output of the program (and of other tests) also goes into the buffer.

Example 4. Replacing input and writing errors to a file

import java.io.*;
import java.nio.charset.StandardCharsets;

public class RedirectDemo {
    public static void main(String[] args) throws IOException {
        System.setIn(new ByteArrayInputStream("42\n".getBytes(StandardCharsets.UTF_8)));
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
        System.out.println("Read: " + in.readLine());

        PrintStream originalErr = System.err;
        try (PrintStream log = new PrintStream(
                new FileOutputStream("errors.log", true), true, StandardCharsets.UTF_8)) { // Java 10+
            System.setErr(log);
            System.err.println("This message goes to errors.log");
            new IllegalStateException("test").printStackTrace(); // the stack trace goes to the file too
        } finally {
            System.setErr(originalErr); // otherwise System.err would point to a closed stream
        }
    }
}
// Console output:
// Read: 42
// errors.log gets the message line and the stack trace java.lang.IllegalStateException: test

The second argument of FileOutputStream (true) turns on appending to the end of the file, and the second argument of PrintStream (true) turns on autoflush. You can also redirect the streams without changing the code, using shell operators (>, 2>, <), as shown in the System.err section.

6. Console gotchas

Mistakes with the standard streams are almost always silent: the program compiles and runs, but prints something unexpected or stops reading input. Most of them come from the fact that System.in returns bytes, and output depends on the locale, the encoding and the method overload.

Mistake What happens What to do instead
int n = System.in.read(); to read a number Input 5 gives 53, input 12 gives 49, and the rest of the line goes to the next read Read a line with readLine() and parse it with Integer.parseInt()
Closing a BufferedReader or Scanner wrapped around System.in (including with try-with-resources) It closes System.in itself; the next read gives IOException: Stream closed (NoSuchElementException for Scanner) and it cannot be reopened Create one wrapper for the whole program and do not close it
System.out.close() All later output silently disappears: PrintStream throws no exceptions Do not close the standard streams; for your own streams use try-with-resources
System.setOut(...) without restoring The output of the rest of the program or of the next tests goes into a foreign stream Save the original stream and restore it in finally
printf("%.2f", x) for a machine format (CSV, JSON) In a locale with a decimal comma it prints 3,14 instead of 3.14 System.out.printf(Locale.ROOT, "%.2f%n", x)

What does this code print?

char[] word = {'J', 'a', 'v', 'a'};
System.out.println(word);
System.out.println("Word: " + word);
System.out.printf("%.2f%n", 3.14159);

Answer (default locale is German):

Java
Word: [C@1b6d3586
3,14

The first line calls the overload println(char[]), which prints the characters of the array. In the second line the array first takes part in concatenation and becomes a string through Object.toString(): [C is the type name of char[], and the hexadecimal number after @ is the identity hash code, which may differ between runs and JVMs. The fix is "Word: " + new String(word) or String.valueOf(word). The third line uses the default locale, so on a German system the decimal separator is a comma, while on a US system it prints 3.14.

Frequently asked questions

Why is System.out.println slow in big loops?

System.out is created with autoflush: every println() flushes the buffer and calls into the OS, and the PrintStream methods are also synchronized. A million println() calls can take seconds. For large output, collect the text in a StringBuilder and print it once, or write through new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))) with a single flush() at the end.

How do I read a password from the console without showing it on the screen?

Use the class java.io.Console: Console console = System.console(); and then char[] pwd = console.readPassword("Password: ");. The typed characters are not echoed, and the password comes back as a char[] that you can overwrite after the check. Without a terminal (in an IDE, with redirected input) System.console() returns null up to Java 21. In Java 22–24 the default JLine-based implementation may return a Console object even then, which is why Java 22 added Console.isTerminal(); Java 25 switched back to the built-in implementation. A check that works on every version: console != null, plus console.isTerminal() on Java 22+.

Why do non-ASCII characters look garbled in the Windows console?

The Windows console uses a legacy code page (for example cp437, cp850 or cp866, depending on the system locale), while Java encodes the output of System.out with its own charset. Java 18 made UTF-8 the default charset (JEP 400), but this did not change the standard streams: since Java 19 their encoding is set by the properties stdout.encoding and stderr.encoding. The usual fix is to switch the console to UTF-8 with chcp 65001 and then either run the program with -Dstdout.encoding=UTF-8 or print through your own stream: new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8).

What is IO.println() and will it replace System.out.println()?

Java 25 (JEP 512) added the class java.lang.IO with the methods print(), println() and readln() for simple programs and teaching: String name = IO.readln("Name: ");. It writes through System.out and reads from System.in, so System.setOut() redirects IO.println() too. The standard streams remain the foundation: System.err, printf() formatting and capturing output in tests are available only through them.

Comments

Please log in or register to have a possibility to add comment.