Console
Console is Java’s built-in class for interacting directly with the terminal. Its killer feature is readPassword(), which reads sensitive input — passwords, PINs, secret keys — without echoing characters to the screen. You also get familiar printf-style formatting and a clean, simple API that doesn’t require closing or exception handling for every call.
What Is Console?
java.io.Console was introduced in Java 6. It represents the character-based console device attached to the JVM — typically your terminal window. You never construct a Console yourself; the JVM gives you a singleton via:
Console console = System.console();
Warning:
System.console()returnsnullwhen there is no interactive terminal — for example when your program’s output is piped, redirected, or when you run inside most IDEs (IntelliJ IDEA, Eclipse, VS Code integrated terminal). Always null-check before using it.
Console console = System.console();
if (console == null) {
System.err.println("No console available. Run from a real terminal.");
System.exit(1);
}
Reading Text Input
Console provides two methods for reading text:
| Method | Returns | Description |
|---|---|---|
readLine() | String | Reads a full line (or null on EOF) |
readLine(String fmt, Object... args) | String | Prints a prompt, then reads a line |
import java.io.Console;
public class GreetUser {
public static void main(String[] args) {
Console console = System.console();
if (console == null) {
System.err.println("No console. Run from a terminal.");
return;
}
String name = console.readLine("Enter your name: ");
int age = Integer.parseInt(console.readLine("Enter your age: "));
console.printf("Hello, %s! You are %d years old.%n", name, age);
}
}
Output (example terminal run):
Enter your name: Alice
Enter your age: 30
Hello, Alice! You are 30 years old.
The prompt variant of readLine(fmt, args) is handy because it prints the prompt and reads the response in a single, atomic call — no separate print + readLine needed.
Reading Passwords Securely
This is where Console truly shines. readPassword() reads input without displaying characters on screen — the terminal is put into a special no-echo mode for the duration of the call.
import java.io.Console;
import java.util.Arrays;
public class SecureLogin {
public static void main(String[] args) {
Console console = System.console();
if (console == null) {
System.err.println("No console available.");
return;
}
String username = console.readLine("Username: ");
char[] password = console.readPassword("Password: ");
// Use the password as a char array — never convert to String
boolean valid = authenticate(username, password);
// Zero out the password array immediately after use
Arrays.fill(password, '\0');
if (valid) {
console.printf("Welcome, %s!%n", username);
} else {
console.printf("Invalid credentials.%n");
}
}
static boolean authenticate(String user, char[] pass) {
// Replace with real credential check
return "admin".equals(user) && Arrays.equals(pass, "secret".toCharArray());
}
}
Output (example terminal run — password is invisible):
Username: admin
Password:
Welcome, admin!
Tip:
readPassword()returns achar[], not aString. This is intentional — see the security note below for why you should always usechar[]and zero it out immediately after use.
Why char[] and Not String?
This is a common interview question. Java String objects are immutable and interned in the String Pool. Once a password is stored in a String, you cannot reliably erase it from memory — it sits on the heap until the garbage collector decides to collect it, and may even appear in heap dumps or thread dumps.
A char[], by contrast, is mutable. You can overwrite every character with '\0' the moment you are done with the password, reducing the window during which sensitive data lives in memory.
char[] password = console.readPassword("Password: ");
// ... use the password ...
Arrays.fill(password, '\0'); // sensitive data is gone
Formatted Output with printf()
Console has its own printf() method that writes directly to the console’s character stream:
console.printf("%-20s %5d items%n", "Widget A", 42);
console.printf("%-20s %5d items%n", "Widget B", 107);
Output:
Widget A 42 items
Widget B 107 items
printf() supports the same format specifiers as PrintStream and String.format() — %s, %d, %f, %.2f, %n, and so on. Use %n rather than "\n" for portable line endings.
Console also exposes format() as an alias for printf() — they are identical.
Getting the Underlying Reader and Writer
If you need access to the raw character streams for advanced use cases, Console exposes them directly:
Console console = System.console();
// java.io.Reader — unbuffered read from the console
java.io.Reader reader = console.reader();
// java.io.PrintWriter — write to the console
java.io.PrintWriter writer = console.writer();
You might use the PrintWriter to integrate with code that expects a Writer, or the Reader to implement your own line-reading loop. These methods do not throw exceptions and do not need to be closed — Console manages their lifecycle.
Handling EOF and Cancelled Input
readLine() returns null when the end-of-stream is reached (the user presses Ctrl+D on Linux/macOS or Ctrl+Z on Windows). Always guard against null in loops:
Console console = System.console();
if (console == null) return;
String line;
while ((line = console.readLine("Enter value (empty to quit): ")) != null) {
if (line.isEmpty()) break;
console.printf("You entered: %s%n", line);
}
console.printf("Goodbye!%n");
Output (example run):
Enter value (empty to quit): hello
You entered: hello
Enter value (empty to quit):
Goodbye!
Note:
readPassword()also returnsnullon EOF. A null result means the stream was closed, not that the user entered an empty password.
Console vs Scanner vs BufferedReader
All three can read from standard input, but they serve different purposes:
| Feature | Console | Scanner | BufferedReader |
|---|---|---|---|
| No-echo password input | Yes (readPassword()) | No | No |
| Formatted prompt + read | Yes (built-in) | No | No |
| Works when piped/redirected | No (null) | Yes | Yes |
| Works inside IDE | No (null) | Yes | Yes |
| Typed parsing (nextInt, etc.) | No — manual | Yes | No — manual |
| Performance | Fast | Moderate | Fast |
| Thread-safe | Yes | No | No |
| Checked exceptions | No | No | Yes (IOException) |
Use Console when you are writing an interactive terminal tool and need either secure password input or formatted prompts. Use Scanner or BufferedReader when the input source may be a file, pipe, or IDE — i.e., anywhere System.console() might return null.
Under the Hood
Console is implemented as a thin wrapper around two native-backed streams: a LineReader for input and a PrintWriter for output. Both are connected to the JVM’s underlying file descriptors for the terminal device — /dev/tty on Unix and CONIN$/CONOUT$ on Windows.
The no-echo mode used by readPassword() is achieved by invoking native OS calls:
- On Unix/Linux/macOS:
Consolecallstcgetattr/tcsetattr(POSIX terminal control) to disable theECHOflag, reads the password, and then re-enables it in afinally-style block. - On Windows: It calls
SetConsoleModeto clear theENABLE_ECHO_INPUTflag.
This native round-trip is why System.console() returns null for piped streams — there is no terminal device to configure, so the JVM simply has nothing to attach to.
Thread Safety
Unlike Scanner, Console is thread-safe. Its readLine() and readPassword() calls are synchronized, making it safe to use from multiple threads — though in practice, interactive console programs are almost always single-threaded.
No Buffering on Output
The PrintWriter returned by console.writer() has auto-flush enabled. Every println() or printf() call flushes immediately to the terminal. This is intentional for interactive use — you never want a prompt to sit in a buffer while the user waits.
Quick Example: Password Change Utility
Here is a complete example that ties everything together — prompting for a current password, a new password, and a confirmation:
import java.io.Console;
import java.util.Arrays;
public class ChangePassword {
public static void main(String[] args) {
Console con = System.console();
if (con == null) {
System.err.println("Run from a real terminal.");
return;
}
char[] current = con.readPassword("Current password: ");
if (!verify(current)) {
Arrays.fill(current, '\0');
con.printf("Wrong password.%n");
return;
}
Arrays.fill(current, '\0');
char[] newPass = con.readPassword("New password: ");
char[] confirm = con.readPassword("Confirm new password: ");
if (Arrays.equals(newPass, confirm)) {
save(newPass);
con.printf("Password changed successfully.%n");
} else {
con.printf("Passwords do not match.%n");
}
Arrays.fill(newPass, '\0');
Arrays.fill(confirm, '\0');
}
static boolean verify(char[] pass) {
return Arrays.equals(pass, "old123".toCharArray());
}
static void save(char[] pass) {
// Hash and persist to your credential store
}
}
Related Topics
- Scanner — flexible input reading from keyboard, files, and strings when
Consoleis unavailable - BufferedReader — fast line-by-line reading for piped or redirected input
- PrintStream —
System.outandSystem.errfor general-purpose console output - PrintWriter — character-stream writer that
Consoleexposes viawriter() - String Immutability — why passwords should stay in
char[]rather thanString - Java I/O — overview of the entire Java I/O framework