Understanding Java I/O: Streams, Decorator Pattern, NIO.2, and Asynchronous I/O
This article provides a comprehensive guide to Java I/O, explaining the distinction between byte and character streams, the role of abstract and concrete stream classes, how the Decorator pattern extends functionality, the enhancements introduced in NIO.2, and the use of asynchronous I/O with Futures and Callbacks.
Reference Materials
The content is based on Oracle's official Java SE 8 documentation, with links to the Java I/O, NIO, and NIO.2 guides.
Preface
The author, a veteran Java developer, admits to forgetting many details of Java I/O due to its extensive class hierarchy and the pervasive use of the Decorator pattern.
Byte Streams vs. Character Streams
Byte streams ( InputStream , OutputStream ) handle raw bytes, while character streams ( Reader , Writer ) handle Unicode characters. The abstract classes cannot be instantiated directly; concrete subclasses such as FileInputStream , FileReader , etc., are used.
Example of creating a nested BufferedReader (illustrative only):
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("somefile.txt")));Typical usage for reading from a URL:
URL cnblogs = new URL("http://www.cnblogs.com/");
InputStream in = cnblogs.openStream();
Reader reader = new InputStreamReader(in);From Abstract to Concrete: Data Sources and Destinations
Various concrete stream classes exist for different sources (files, memory, pipes, network) and destinations. UML diagrams illustrate the class relationships.
Extending Functionality with the Decorator Pattern
All streams support read() and write() . To add buffering, line reading, or push‑back capabilities, wrapper classes such as BufferedInputStream , BufferedReader , PushbackInputStream , etc., are used.
Data Streams Example
Using DataOutputStream and DataInputStream to write and read primitive values in binary form:
package com.xkland.sample;
import java.io.*;
public class DataStreamsDemo {
public static void writeToFile(String filename) {
double[] prices = {19.99, 9.99, 15.99, 3.99, 4.99};
int[] units = {12, 8, 13, 29, 50};
String[] descs = {"Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain"};
try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename)))) {
for (int i = 0; i < prices.length; i++) {
out.writeDouble(prices[i]);
out.writeInt(units[i]);
out.writeUTF(descs[i]);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void readFromFile(String filename) {
double price;
int unit;
String desc;
double total = 0.0;
try (DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(filename)))) {
while (true) {
price = in.readDouble();
unit = in.readInt();
desc = in.readUTF();
System.out.format("You ordered %d units of %s at $%.2f%n", unit, desc, price);
total += unit * price;
}
} catch (EOFException e) {
System.out.format("All data read, total price: $%.2f%n", total);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}The same data can be written in a human‑readable text format using PrintStream and read back with Scanner :
package com.xkland.sample;
import java.io.*;
import java.util.Scanner;
public class PrintStreamDemo {
public static void writeToFile(String filename) {
double[] prices = {19.99, 9.99, 15.99, 3.99, 4.99};
int[] units = {12, 8, 13, 29, 50};
String[] descs = {"Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain"};
try (PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(filename)))) {
for (int i = 0; i < prices.length; i++) {
out.println(prices[i]);
out.println(units[i]);
out.println(descs[i]);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void readFromFile(String filename) {
double price;
int unit;
String desc;
double total = 0.0;
try (Scanner s = new Scanner(new BufferedReader(new FileReader(filename)))) {
s.useDelimiter("\n");
while (s.hasNext()) {
price = s.nextDouble();
unit = s.nextInt();
desc = s.next();
System.out.format("You ordered %d units of %s at $%.2f%n", unit, desc, price);
total += unit * price;
}
System.out.format("All data read, total price: $%.2f%n", total);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}Java 7 NIO.2 Enhancements
NIO.2 introduces Path , Paths , and Files for platform‑independent file manipulation, including path resolution, normalization, and attribute access.
Path absolute = Paths.get("/", "home", "youxia");
Path relative = Paths.get("myprog", "conf", "user.properties");
Path combined = absolute.resolve(relative); // /home/youxia/myprog/conf/user.propertiesCommon file operations (create, copy, move, delete) are performed via static methods in Files . Symbolic link handling and POSIX attributes are demonstrated with Files.isSymbolicLink and Files.readAttributes .
Asynchronous I/O in NIO.2
Java 7 adds three asynchronous channel types. The article shows reading a file with a Future and with a CompletionHandler callback.
try {
Path file = Paths.get("/home/youxia/testfile");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
Future
result = channel.read(buffer, 0);
while (!result.isDone()) {
// do other work
}
int bytesRead = result.get();
System.out.println("Bytes read: " + bytesRead);
} catch (IOException | ExecutionException | InterruptedException e) {
e.printStackTrace();
}Callback version:
channel.read(buffer, 0, buffer, new CompletionHandler
() {
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("Bytes read: " + result);
}
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println(exc.getMessage());
}
});Conclusion
The author hopes the detailed walkthrough will help readers retain the concepts of Java I/O, from basic streams to NIO.2 and asynchronous operations, and encourages feedback and support.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.