🏠 Home I/O and NIO Stream I/O Classes Hierarchy
Card 4 of 4
Next
I/O AND NIO

Stream I/O Classes Hierarchy

Rule: Java I/O uses decorator pattern with byte streams (InputStream/OutputStream) and character streams (Reader/Writer).

  • Byte streams: Handle raw binary data (images, videos, executables)
  • Character streams: Handle text data with encoding support
  • Buffered streams: Add buffering for performance
  • Bridge streams: Convert between byte and character streams
import java.io.*;

// Byte streams hierarchy
// InputStream <- FileInputStream, BufferedInputStream, ObjectInputStream
// OutputStream <- FileOutputStream, BufferedOutputStream, ObjectOutputStream

// Reading bytes
try (FileInputStream fis = new FileInputStream("data.bin");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    
    int byteValue;
    while ((byteValue = bis.read()) != -1) {
        // Process byte (0-255 or -1 for EOF)
        System.out.print(byteValue + " ");
    }
}

// Writing bytes
try (FileOutputStream fos = new FileOutputStream("output.bin");
     BufferedOutputStream bos = new BufferedOutputStream(fos)) {
    
    byte[] data = {65, 66, 67, 68, 69}; // ASCII for ABCDE
    bos.write(data);
    bos.flush(); // Ensure data is written
}

// Character streams hierarchy
// Reader <- FileReader, BufferedReader, InputStreamReader
// Writer <- FileWriter, BufferedWriter, OutputStreamWriter

// Reading characters
try (FileReader fr = new FileReader("text.txt");
     BufferedReader br = new BufferedReader(fr)) {
    
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

// Writing characters
try (FileWriter fw = new FileWriter("output.txt");
     BufferedWriter bw = new BufferedWriter(fw)) {
    
    bw.write("Hello World");
    bw.newLine();
    bw.write("Java I/O");
    bw.flush();
}

Bridge streams - converting between byte and character:

// InputStreamReader - byte stream to character stream
try (FileInputStream fis = new FileInputStream("input.txt");
     InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
     BufferedReader br = new BufferedReader(isr)) {
    
    String line = br.readLine();
    System.out.println(line);
}

// OutputStreamWriter - character stream to byte stream  
try (FileOutputStream fos = new FileOutputStream("output.txt");
     OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
     BufferedWriter bw = new BufferedWriter(osw)) {
    
    bw.write("Unicode text: 你好");
    bw.flush();
}

// System streams
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
PrintWriter output = new PrintWriter(System.out);

String userInput = console.readLine();
output.println("You entered: " + userInput);
output.flush();

Object serialization:

// Serializable class
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

// Write object
try (FileOutputStream fos = new FileOutputStream("person.ser");
     ObjectOutputStream oos = new ObjectOutputStream(fos)) {
    
    Person person = new Person("Alice", 30);
    oos.writeObject(person);
}

// Read object
try (FileInputStream fis = new FileInputStream("person.ser");
     ObjectInputStream ois = new ObjectInputStream(fis)) {
    
    Person person = (Person) ois.readObject();
    System.out.println(person); // Person{name='Alice', age=30}
}

Performance considerations:

// ❌ Slow - unbuffered I/O
try (FileReader fr = new FileReader("largefile.txt")) {
    int ch;
    while ((ch = fr.read()) != -1) {  // Each read() is a system call
        System.out.print((char) ch);
    }
}

// ✅ Fast - buffered I/O
try (FileReader fr = new FileReader("largefile.txt");
     BufferedReader br = new BufferedReader(fr)) {
    
    int ch;
    while ((ch = br.read()) != -1) {  // Reads from internal buffer
        System.out.print((char) ch);
    }
}

// ✅ Fastest - read lines at once
try (FileReader fr = new FileReader("largefile.txt");
     BufferedReader br = new BufferedReader(fr)) {
    
    String line;
    while ((line = br.readLine()) != null) {  // Read entire lines
        System.out.println(line);
    }
}

💡 Learning Tip: Remember “BYTE vs CHAR, BUFFER for SPEED” - use byte streams for binary data, character streams for text, and always add buffering for performance.

Q: When should you use InputStreamReader vs FileReader?
A: Use InputStreamReader when you need explicit encoding control or are wrapping a byte stream. FileReader is a convenience class that uses the platform default encoding.