Study guide

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering

Oracle Java SE 17 Developer Certified Professional 1Z0-829

Track your progress as you read

Finished sections get a check mark.

1 Introduction: The 1Z0-829 Exam

About the Java SE 17 Developer (1Z0-829) Exam

2 Chapter 1: Java Fundamentals & Language Basics

1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting 1.2 Local Variable Type Inference (`var`) Rules and Scoping 1.3 Operators, Precedence, Evaluation Order, and Short-Circuit Evaluation 1.4 Text Blocks, String Immutability, and StringBuilder APIs

3 Chapter 2: Program Flow & Decision Constructs

2.1 Conditional Branching with if/else and the Ternary Operator 2.2 Switch Statements and Switch Expressions 2.3 Iteration Constructs, Loop Control, and Branching Labels

4 Chapter 3: Object-Oriented Design & Class Mechanics

3.1 Class Declarations, Field/Method Signatures, and Access Modifiers 3.2 Constructors, Overloading, Initialization Order, and Garbage Collection 3.3 Inheritance, Method Overriding, Polymorphism, and `super`/`this` 3.4 Abstract Classes, Static Members, and Object Methods (`equals`, `hashCode`, `toString`)

5 Chapter 4: Advanced OOP: Interfaces, Records & Sealed Types

4.1 Interface Architecture: Default, Static, and Private Methods 4.2 Enumerations: Constants, Constructors, Methods, and Custom Bodies 4.3 Records: Canonical/Compact Constructors, Components, and Immutability 4.4 Sealed Classes and Interfaces: `permits`, Hierarchy Constraints, and Modifiers

6 Chapter 5: Pattern Matching & Nested Types

5.1 Pattern Matching for `instanceof` and Flow Scoping Mechanics 5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes 5.3 Defensive Programming, Immutability Patterns, and Validation

7 Chapter 6: Generics, Collections & Arrays

6.1 Generic Classes, Interfaces, Methods, and Type Erasure 6.2 Bounded Type Parameters and Wildcards (PECS Rule) 6.3 Collections Framework: List, Set, Queue, and Deque APIs 6.4 The Map Interface, Sorting, Comparable, and Comparator 6.5 Arrays: Declaration, Initialization, Multidimensional Arrays, and the Arrays Utility Class

8 Chapter 7: Functional Programming & Lambda Expressions

7.1 Functional Interfaces and Lambda Syntax Rules 7.2 Standard Functional Interfaces in `java.util.function` 7.3 Method References: Static, Bound, Unbound, and Constructor References

9 Chapter 8: The Stream API

8.1 Stream Lifecycle: Creation, Pipeline Architecture, and Lazy Evaluation 8.2 Intermediate Stream Operations: Filtering, Mapping, Sorting, and Slicing 8.3 Terminal Stream Operations: Reduction, Matching, Finding, and Collecting 8.4 Primitive Streams (`IntStream`, `LongStream`, `DoubleStream`) and Numeric Statistics

10 Chapter 9: Advanced Streams & Collectors

9.1 The Optional Class: Safe Null Handling, Transformation, and Chaining 9.2 The Collectors API: Downstream Reductions, Grouping, and Partitioning 9.3 Parallel Streams: Fork/Join Pool, Spliterators, and Concurrency Pitfalls

11 Chapter 10: Exception Handling

10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors 10.2 Exception Constructs: `try-catch-finally`, Multi-Catch, and Rethrowing 10.3 Try-With-Resources, `AutoCloseable`, Suppressed Exceptions, and Custom Exceptions

12 Chapter 11: Date/Time API & Localization

11.1 Date and Time Core Types: `LocalDate`, `LocalTime`, `LocalDateTime` 11.2 Timestamps, Intervals, and Durations: `Instant`, `Period`, and `Duration` 11.3 Time Zones, Daylight Saving Time (DST) Offsets, and `ZonedDateTime` 11.4 Internationalization and Localization: `Locale`, `ResourceBundle`, and Formatters 11.5 Formatting and Parsing Messages, Numbers, Percentages, and Localized Dates

13 Chapter 12: Java Platform Module System (JPMS)

12.1 Module Architecture, `module-info.java`, and Module Paths 12.2 Module Directives: `requires`, `requires transitive`, `exports`, and Qualified Exports 12.3 Reflection, Deep Encapsulation, `opens`, and Open Modules 12.4 Services in JPMS (`uses`, `provides...with`), Migration Paths, and Tooling (`jlink`, `jdeps`) 12.5 Compiling and Packaging Code: Non-Modular JARs, Modular JARs, and Migration Strategies

14 Chapter 13: Concurrency & Multithreading

13.1 Thread Fundamentals, `Runnable`, `Callable`, and Thread Lifecycle 13.2 Memory Visibility, `volatile`, Intrinsic Locks, and Synchronization 13.3 The Concurrency Framework: `ExecutorService`, Thread Pools, and `Future` 13.4 Concurrent Collections, Atomic Variables, Explicit Locks, and `CompletableFuture`

15 Chapter 14: Java I/O and NIO.2

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering 14.2 Object Serialization, `Serializable`, `transient`, and `serialVersionUID` 14.3 NIO.2 Architecture: `Path`, `Paths`, and Basic `Files` Operations 14.4 Advanced NIO.2: Directory Walking, Streams (`Files.lines`, `walk`), and `WatchService`

16 Chapter 15: Accessing Databases with JDBC

15.1 JDBC Architecture, Drivers, `DriverManager`, and `DataSource` Connections 15.2 SQL Execution: `Statement`, `PreparedStatement`, and `CallableStatement` 15.3 `ResultSet` Navigation, Data Types, and Transaction Management (Commit, Rollback, Savepoints)

17 Chapter 16: Annotations & the Java Logging API

16.1 Standard Annotations: @Override, @FunctionalInterface, @Deprecated, @SuppressWarnings, and @SafeVarargs 16.2 Java Logging API Basics: Logger, Levels, Handlers, and Configuration

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering

Key Takeaways Explain This Topic AI

Java classical I/O is split into byte streams (InputStream/OutputStream) for raw 8-bit binary data and character streams (Reader/Writer) for 16-bit Unicode characters.

Low-level node streams (FileInputStream, FileReader) connect directly to physical data sources, whereas high-level decorator streams (BufferedInputStream, BufferedReader, PrintWriter) wrap underlying streams to provide buffering, line reading, and character encoding conversions.

In a decorator stream pipeline, closing the outermost wrapper stream automatically flushes buffers and closes all wrapped underlying streams.

java.io.Console via System.console() provides direct console access, returning null in non-interactive environments (such as IDEs or background processes); readPassword() returns char[] to allow prompt in-memory wiping.

In Java 17 the default charset still depends on the platform or -Dfile.encoding; UTF-8 became the universal default only in Java 18 (JEP 400), so portable Java 17 code passes StandardCharsets.UTF_8 explicitly.

Last updated: October 2026

14.1 Classical I/O: Byte and Character Streams, Readers, Writers, and Buffering

The Java I/O framework in java.io provides a stream-based abstraction for transferring data between memory and external destinations such as disks, network sockets, or in-memory arrays. Streams represent an ordered sequence of data. To succeed on the 1Z0-829 exam, developers must master the architectural bifurcation between byte streams and character streams, understand the decorator pattern that connects low-level node streams to high-level filter streams, manage stream flushing and closing lifecycles, and handle interactive terminal access via java.io.Console .

The Stream Architecture: Byte Streams vs. Character Streams

Java I/O separates I/O operations into two distinct class hierarchies rooted at four abstract classes: java.io Root Hierarchies

  • |
  • +----------------------------+----------------------------+
  • | |
  • [Byte Streams] [Character Streams]
  • (8-bit raw bytes) (16-bit Unicode characters)
  • |\ |
  • InputStream OutputStream Reader Writer
  • Byte Streams ( InputStream and OutputStream )

Byte streams process raw binary data 8 bits at a time. They are the foundation for reading and writing binary files (such as images, compiled .class files, compressed ZIP archives, and audio files) or raw network socket protocols.

InputStream : The abstract superclass of all byte input streams. Key methods include:

int read() : Reads the next byte of data. Returns an int in the range 0 to 255 , or -1 if the end of the stream (EOF) has been reached.

int read(byte[] b) : Reads up to b.length bytes into the byte array, returning the total number of bytes read, or -1 if at EOF.

int read(byte[] b, int off, int len) : Reads up to len bytes into the array starting at offset off .

void close() : Closes the stream and releases operating system resources.

OutputStream : The abstract superclass of all byte output streams. Key methods include:

void write(int b) : Writes the specified byte (the 8 low-order bits of the integer argument; the upper 24 bits are ignored).

void write(byte[] b) / void write(byte[] b, int off, int len) : Writes byte arrays to the stream.

void flush() : Forces any buffered output bytes to be written out to the underlying destination.

void close() : Flushes and closes the stream.

Character Streams ( Reader and Writer )

Character streams operate on 16-bit Unicode characters ( char ). They handle character encodings automatically, translating raw bytes from the underlying storage into Unicode characters and vice-versa using a specified or default Charset .

Reader : The abstract superclass for reading character streams.

int read() : Reads a single character. Returns an int in the range 0 to 65535 ( 0x0000 to 0xFFFF ), or -1 if at EOF.

int read(char[] cbuf) / int read(char[] cbuf, int off, int len) : Reads characters into a character array.

Writer : The abstract superclass for writing character streams.

void write(int c) : Writes a single character (the 16 low-order bits).

void write(char[] cbuf) : Writes an array of characters.

void write(String str) / void write(String str, int off, int len) : Directly writes a String to the stream without requiring manual array conversion.

  • Feature Byte Streams ( InputStream / OutputStream ) Character Streams ( Reader / Writer )
  • Data Unit 8-bit raw bytes 16-bit Unicode characters ( char )
  • Target Data Binary files (images, audio, zip, compiled classes) Text files (JSON, XML, CSV, source code, logs)
  • Encoding No charset translation; raw binary bytes Translates between bytes and characters using a Charset
  • Direct String Writing No write(String) method exists Provides write(String) directly on Writer
  • EOF Indicator read() returns -1 read() returns -1

Warning

The Byte Casting Trap in read() Loops Notice that read() returns an int , not a byte or char . If you cast the result of read() directly to a byte before checking for EOF, a byte containing 0xFF (value 255) will be sign-extended to (byte) -1 , causing the loop to terminate prematurely! Always store the return value in an int variable, test against -1 , and only then cast to byte or char : int b;

  • while ((b = in.read()) != -1) {
  • byte actualByte = (byte) b; // Correct: EOF checked before casting
  • }
  • Node Streams vs. High-Level Filter/Decorator Streams

Java's I/O framework uses the Decorator Pattern . Classes are categorized into:

Node Streams (Low-Level) : Connect directly to physical data sources or sinks (files, memory buffers, pipes).

Filter / Decorator Streams (High-Level) : Wrap around an existing stream to add capabilities such as buffering, line-oriented reading, primitive data parsing, or character encoding bridging.

  • Stream Category Byte Input Byte Output Character Input Character Output
  • File Node FileInputStream FileOutputStream FileReader FileWriter

Memory Node ByteArrayInputStream ByteArrayOutputStream CharArrayReader , StringReader CharArrayWriter , StringWriter

  • Buffering Filter BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
  • Bridge Filter InputStreamReader (byte → \to → char) OutputStreamWriter (char → \to → byte) — —
  • Print / Formatting — PrintStream ( System.out ) — PrintWriter
  • Object Serialization ObjectInputStream ObjectOutputStream — —
  • Chaining Decorators and Proper Wrapping Order

To construct an efficient, buffered text reader that reads UTF-8 encoded files, developers wrap a low-level byte stream in a character bridge stream, which is then wrapped in a buffered reader: import java.io.*;

import java.nio.charset.StandardCharsets;

  • public class StreamChainingExample {
  • public static void readTextFile(String filePath) throws IOException {
  • // Low-level node stream: FileInputStream
  • // Bridge stream: InputStreamReader (converts bytes to chars using UTF-8)
  • // High-level buffering stream: BufferedReader (provides readLine())
  • try (BufferedReader reader = new BufferedReader(
  • new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))) {
  • String line;
  • while ((line = reader.readLine()) != null) {
  • System.out.println(line);
  • }
  • }
  • }
  • }

In Java 11+, FileReader also provides constructors accepting a Charset directly: try (BufferedReader reader = new BufferedReader(new FileReader(filePath, StandardCharsets.UTF_8))) {

  • // Convenient equivalent wrapper
  • }
  • The Closing Cascading Rule

When decorators are wrapped around underlying streams, closing the outermost wrapper stream automatically flushes and closes all underlying wrapped streams .

In the example above, calling reader.close() in try-with-resources closes the BufferedReader , which immediately closes the InputStreamReader , which in turn closes the underlying FileInputStream . You do not need to (and should not) close inner streams separately.

Buffering and Flushing Mechanics

Unbuffered I/O issues a separate operating system call for every individual byte or character read or written. This causes significant performance degradation. BufferedInputStream , BufferedOutputStream , BufferedReader , and BufferedWriter maintain an internal memory buffer (defaulting to 8,192 bytes or characters):

On reads, the buffer fills from the underlying resource in bulk, and subsequent read() calls consume directly from fast RAM.

On writes, bytes accumulate in the buffer. Data is written to disk only when the buffer fills, or when flush() is explicitly invoked.

BufferedReader.readLine() : Reads a line of text terminated by \n , \r , or \r\n . Returns null when EOF is reached (unlike read() which returns -1 ).

BufferedWriter.newLine() : Writes a platform-specific line separator ( \r\n on Windows, \n on Linux/macOS).

PrintStream and PrintWriter Error Suppression

PrintStream (which types System.out and System.err ) and PrintWriter format and print text representations of primitives and objects ( print() , println() , printf() , format() ).

Warning

The PrintStream / PrintWriter Exception Trap Unlike almost all other java.io classes, PrintStream and PrintWriter never throw checked IOException from their formatting methods! Instead, they swallow the exception and set an internal error flag. To verify if an I/O error occurred, callers must explicitly invoke boolean error = writer.checkError(); .

Interactive Console Access: java.io.Console

Java provides java.io.Console to handle interactive user input and formatted output in command-line environments. A singleton instance is retrieved via System.console() .

The System.console() Null Trap

System.console() returns the console object associated with the current JVM process. However, if the JVM is running in a non-interactive environment , System.console() returns null !

Scenarios where System.console() returns null :

The program runs inside an IDE (such as IntelliJ IDEA, Eclipse, or NetBeans) where standard streams are redirected to internal console tabs.

Standard input or output is redirected via shell pipes or redirection operators ( java MainApp < input.txt or java MainApp | grep error ).

The application runs as a background daemon, cron job, or CI/CD container without an allocated virtual terminal (TTY). Console console = System.console();

  • if (console == null) {
  • System.err.println("No interactive console available; falling back to Scanner/System.in");
  • return;
  • }
  • Reading Text and Passwords

Console provides dedicated methods for user interaction:

String readLine(String fmt, Object... args) : Prints a formatted prompt and reads a line of text entered by the user.

char[] readPassword(String fmt, Object... args) : Disables character echoing on the terminal so onlookers cannot see the characters typed, and returns a char[] rather than a String . Console console = System.console();

  • if (console != null) {
  • String username = console.readLine("Enter username: ");
  • char[] password = console.readPassword("Enter password for %s: ", username);
  • try {
  • // Process authentication using password char array
  • } finally {
  • // SECURITY REQUIREMENT: Wipe password array immediately from memory
  • java.util.Arrays.fill(password, ' ');
  • }
  • }
  • Why readPassword() Returns char[] Instead of String

The reason is security and memory management:

String is immutable : Once created, a String object cannot be modified or cleared. It remains in the JVM heap (and potentially in the String intern pool) until garbage collected, which may not occur for an indeterminate period. During this window, an unauthorized heap dump or memory scanner can extract the plaintext password.

char[] is mutable : The character array can be immediately overwritten with zeroes or blanks ( Arrays.fill(password, ' ') ) as soon as authentication finishes, completely erasing the sensitive credentials from memory.

Character Encodings and StandardCharsets

A charset defines the mapping between sequences of 16-bit Unicode characters ( char ) and sequences of 8-bit bytes ( byte ).

The standard constants are defined in java.nio.charset.StandardCharsets :

StandardCharsets.UTF_8 : Eight-bit UCS Transformation Format. Variable-length (1 to 4 bytes per code point). Supports all Unicode characters.

StandardCharsets.UTF_16 : Sixteen-bit UCS Transformation Format (with byte-order mark).

StandardCharsets.US_ASCII : Seven-bit ASCII (values 0–127).

StandardCharsets.ISO_8859_1 : ISO Latin Alphabet No. 1 (values 0–255).

Default Charset in Java 17 (Before JEP 400)

In Java 17 , classes such as FileReader , FileWriter , InputStreamReader , OutputStreamWriter , and PrintStream use the platform default charset when you do not pass one. That default comes from the operating system locale or the file.encoding system property (for example windows-1252 on many Western Windows machines and UTF-8 on most Linux and macOS systems), so the same file can decode differently on two machines.

JEP 400 (UTF-8 by Default) was delivered in Java 18, not Java 17. From Java 18 onward, the default charset is UTF-8 on every platform unless file.encoding=COMPAT is set.

The NIO.2 convenience methods in java.nio.file.Files ( readAllLines , lines , readString , writeString , newBufferedReader , newBufferedWriter ) already default to UTF-8 in Java 17, regardless of the platform charset.

Portable Java 17 code passes the charset explicitly, for example new FileReader(path, StandardCharsets.UTF_8) (Java 11+) or new InputStreamReader(in, StandardCharsets.UTF_8) . Charset.defaultCharset() reports the runtime default.