Ch 15.3 Standard I/O and the File Class
1. Standard I/O Streams
Java provides three pre-built standard streams as static variables in the System class. They are always available without any setup — connected to the console by default.
System.in: A byte-based input stream (InputStream) for reading from the console keyboardSystem.out: A byte-based output stream (PrintStream) for printing to the consoleSystem.err: A separate error output stream — typically displayed in red in IDEs
import java.io.IOException;
public class StandardIOBasic {
public static void main(String[] args) {
// System.out: standard output
System.out.println("Normal message via System.out");
// System.err: error stream (appears separately, often in red)
System.err.println("Error message via System.err");
// System.in: read raw bytes from keyboard
System.out.println("Type characters (Ctrl+Z on Windows / Ctrl+D on Mac/Linux to stop):");
try {
int input;
while ((input = System.in.read()) != -1) {
System.out.print((char) input);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. Reading Text Input: InputStreamReader + BufferedReader
System.in is a raw byte stream. To read text input conveniently (especially line by line), wrap it with an InputStreamReader for encoding conversion and a BufferedReader for readLine().
import java.io.*;
import java.nio.charset.StandardCharsets;
public class ConsoleInputExample {
public static void main(String[] args) throws IOException {
// Chain: InputStream -> InputStreamReader (encoding) -> BufferedReader (lines)
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in, StandardCharsets.UTF_8)
);
System.out.print("Enter your name: ");
String name = br.readLine();
System.out.print("Enter your age: ");
int age = Integer.parseInt(br.readLine().trim());
System.out.printf("Hello, %s! You are %d years old.%n", name, age);
// Repeat until user types "quit"
System.out.println("Enter commands (type 'quit' to exit):");
String line;
while ((line = br.readLine()) != null && !line.equalsIgnoreCase("quit")) {
System.out.println("You entered: " + line.trim().toUpperCase());
}
System.out.println("Goodbye!");
}
}
3. Scanner vs BufferedReader
Both are commonly used for console input, but they have different strengths.
import java.util.Scanner;
import java.io.*;
public class ScannerVsBufferedReader {
public static void main(String[] args) throws IOException {
// Scanner: easy to use, good for parsing tokens
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an integer: ");
int num = scanner.nextInt();
System.out.print("Enter a string: ");
String str = scanner.next();
System.out.println("Got: " + num + ", " + str);
scanner.close();
// BufferedReader: faster for large input, reads whole lines
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in, java.nio.charset.StandardCharsets.UTF_8)
);
System.out.print("Enter a line: ");
String line = br.readLine();
System.out.println("Line: " + line);
}
}
| Aspect | Scanner | BufferedReader |
|---|---|---|
| Ease of use | Very easy | Moderate |
| Token parsing | Built-in (nextInt, nextDouble) | Manual (Integer.parseInt, etc.) |
| Performance | Slower (tokenization overhead) | Faster |
| Best for | Competitive programming, simple input | Performance-critical reading |
| Delimiter | Whitespace by default (configurable) | Line by line |
4. Reading Passwords Securely: Console.readPassword()
System.console() provides a readPassword() method that reads input without echoing it to the screen.
import java.io.Console;
public class SecureInputExample {
public static void main(String[] args) {
Console console = System.console();
if (console == null) {
System.err.println("No console available (running in IDE). Use command line.");
return;
}
String username = console.readLine("Username: ");
char[] password = console.readPassword("Password: "); // input is NOT echoed
System.out.println("Logging in as: " + username);
// Do NOT convert to String if possible — strings are immutable and stay in memory
// Use the char[] directly for security
boolean valid = validatePassword(password);
// Clear the password from memory after use
java.util.Arrays.fill(password, '\0');
if (valid) {
console.printf("Welcome, %s!%n", username);
} else {
console.printf("Invalid credentials.%n");
}
}
static boolean validatePassword(char[] pwd) {
// In a real app, compare against a hashed value
return new String(pwd).equals("secret123");
}
}
System.console() returns null when running in an IDE or when I/O is redirected. Always check for null before using it.
5. Redirecting Standard Streams: System.setIn/Out/Err
You can redirect the standard streams to files or custom streams — useful for logging or testing.
import java.io.*;
public class StreamRedirectExample {
public static void main(String[] args) throws IOException {
// Redirect System.out to a file
PrintStream originalOut = System.out; // save original
try (PrintStream fileOut = new PrintStream(
new BufferedOutputStream(new FileOutputStream("console_output.txt")),
true, "UTF-8")) {
System.setOut(fileOut); // redirect
System.out.println("This goes to the file, not the console!");
System.out.printf("Date: %s%n", java.time.LocalDate.now());
} finally {
System.setOut(originalOut); // restore original
}
System.out.println("Back to normal console output.");
// Redirect System.in to read from a file (useful for testing)
try (InputStream fileIn = new FileInputStream("input.txt")) {
System.setIn(fileIn);
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)
);
// Now br.readLine() reads from "input.txt"
}
}
}
6. The File Class
java.io.File is NOT a stream — it represents a file or directory path and provides metadata queries and file management operations. Java 7+ recommends java.nio.file.Path + Files for new code, but File remains widely used.
import java.io.*;
import java.util.Date;
public class FileClassExample {
public static void main(String[] args) throws IOException {
File file = new File("example.txt");
File dir = new File("myDirectory");
// Create directory if it doesn't exist
if (!dir.exists()) {
boolean created = dir.mkdirs(); // creates all intermediate dirs too
System.out.println("Directory created: " + created);
}
// Create file if it doesn't exist
if (!file.exists()) {
boolean created = file.createNewFile();
System.out.println("File created: " + created);
}
// File metadata
System.out.println("Name: " + file.getName());
System.out.println("Path: " + file.getPath());
System.out.println("Abs path: " + file.getAbsolutePath());
System.out.println("Size: " + file.length() + " bytes");
System.out.println("Readable: " + file.canRead());
System.out.println("Writable: " + file.canWrite());
System.out.println("Is file: " + file.isFile());
System.out.println("Is dir: " + file.isDirectory());
System.out.println("Last mod: " + new Date(file.lastModified()));
// List directory contents
File[] contents = dir.listFiles();
if (contents != null) {
for (File f : contents) {
System.out.println(" " + (f.isDirectory() ? "[DIR] " : " ") + f.getName());
}
}
// Rename / move
File renamed = new File("example_renamed.txt");
boolean ok = file.renameTo(renamed);
System.out.println("Renamed: " + ok);
// Delete
renamed.delete();
}
}
7. File as a Stream Source
File objects can be passed to stream constructors as the file source:
import java.io.*;
public class FileAsStreamSource {
public static void main(String[] args) throws IOException {
File file = new File("data.txt");
// Write via File object
try (PrintWriter pw = new PrintWriter(new FileWriter(file))) {
pw.println("Line one");
pw.println("Line two");
}
// Read via File object
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
br.lines().forEach(System.out::println);
}
// BufferedInputStream from File
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[1024];
int n = bis.read(buffer);
System.out.println("Read " + n + " bytes");
}
}
}
8. Practical Example: Simple Text-Based Menu Application
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class MenuApplication {
static final String DATA_FILE = "notes.txt";
static List<String> notes = new ArrayList<>();
public static void main(String[] args) throws IOException {
loadNotes();
BufferedReader input = new BufferedReader(
new InputStreamReader(System.in, StandardCharsets.UTF_8)
);
while (true) {
System.out.println("\n=== Notes App ===");
System.out.println("1. View all notes");
System.out.println("2. Add a note");
System.out.println("3. Delete a note");
System.out.println("4. Save and exit");
System.out.print("Choice: ");
String choice = input.readLine();
if (choice == null) break;
switch (choice.trim()) {
case "1" -> {
if (notes.isEmpty()) System.out.println("No notes yet.");
else for (int i = 0; i < notes.size(); i++) {
System.out.printf("%d. %s%n", i + 1, notes.get(i));
}
}
case "2" -> {
System.out.print("New note: ");
String note = input.readLine();
if (note != null && !note.isBlank()) {
notes.add(note.trim());
System.out.println("Added.");
}
}
case "3" -> {
System.out.print("Delete note number: ");
try {
int idx = Integer.parseInt(input.readLine().trim()) - 1;
if (idx >= 0 && idx < notes.size()) {
System.out.println("Deleted: " + notes.remove(idx));
}
} catch (NumberFormatException e) {
System.out.println("Invalid number.");
}
}
case "4" -> {
saveNotes();
System.out.println("Saved. Goodbye!");
return;
}
default -> System.out.println("Invalid option.");
}
}
}
static void loadNotes() throws IOException {
File file = new File(DATA_FILE);
if (!file.exists()) return;
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
if (!line.isBlank()) notes.add(line);
}
}
System.out.println("Loaded " + notes.size() + " notes.");
}
static void saveNotes() throws IOException {
try (PrintWriter pw = new PrintWriter(
new OutputStreamWriter(new FileOutputStream(DATA_FILE), StandardCharsets.UTF_8))) {
notes.forEach(pw::println);
}
}
}
Pro tip: Use System.console() for interactive terminal applications — it provides secure password input and better terminal integration. For non-interactive programs, parsing System.in with BufferedReader + InputStreamReader gives the best performance and Unicode support.