Back to I/O & System
Q3Question 3 of 3

What is the difference between byte streams and character streams?

Key Concepts
  • Byte streams (InputStream/OutputStream): handle raw binary data – images, PDFs, network bytes
  • Character streams (Reader/Writer): handle text with encoding – automatically convert bytes to/from chars
  • Use character streams for text (UTF-8 CSV files, logs, config), byte streams for binary (images, jar files)
  • BufferedReader/BufferedWriter add buffering for performance
  • Since Java 11: Files.readString() and Files.writeString() simplify text file operations

Definition

Byte streams handle raw bytes (InputStream/OutputStream). Character streams handle text with encoding (Reader/Writer). Use character streams for text, byte streams for binary data.

Two families:

  • Byte streams: InputStream / OutputStream – read/write raw bytes. For binary data: images, PDFs, serialized objects, network protocols.
  • Character streams: Reader / Writer – read/write characters with encoding (UTF-8, UTF-16). For text: CSV files, logs, configuration, SQL scripts.

Byte streams are like transmitting raw bank data in binary format between servers. Character streams are like reading and writing bank letters in a specific language (encoding). You would not read a scanned cheque image as text, and you would not read a CSV report as raw bytes.

Understanding the Concepts

Byte streams

JAVA
// Read a binary file (e.g., scanned cheque image)
try (InputStream is = new FileInputStream("cheque.jpg");
     OutputStream os = new FileOutputStream("copy.jpg")) {
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
        os.write(buffer, 0, bytesRead);
    }
}

Character streams

JAVA
// Read a text file with UTF-8 encoding
try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(new FileInputStream("accounts.csv"), StandardCharsets.UTF_8))) {
    String line;
    while ((line = reader.readLine()) != null) {
        processLine(line);
    }
}

// Simpler since Java 11
String content = Files.readString(Path.of("accounts.csv"), StandardCharsets.UTF_8);
List<String> lines = Files.readAllLines(Path.of("accounts.csv"), StandardCharsets.UTF_8);

Comparison

| Feature | Byte stream | Character stream |
|---|---|---|
| Base classes | InputStream/OutputStream | Reader/Writer |
| Unit | byte (8 bits) | char (16 bits, UTF-16) |
| Encoding | None (raw) | Converts via charset |
| Use for | Binary: images, jars, protocols | Text: CSV, logs, config |
| Buffered | BufferedInputStream | BufferedReader |

What should a senior engineer know?

  • Always specify charset explicitly. new InputStreamReader(is, StandardCharsets.UTF_8). Never rely on the platform default.
  • NIO.2 (java.nio.file) is modern: Files.copy(), Files.readAllBytes(), Files.readString().
  • try-with-resources closes streams automatically. Always use it.
  • Channels and Buffers (java.nio) provide non-blocking I/O for high-performance servers.

What are common mistakes?

A common mistake is not understanding the concept fully before using it in production banking applications.

Wrong approach:

JAVA
package com.bank.service;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, double amount) {
        // WRONG: using double for money
        double balance = getBalance(fromAccount);
        balance = balance - amount;
    }
    private double getBalance(String id) { return 10000.0; }
}

Correct approach:

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // CORRECT: using BigDecimal for money
        BigDecimal balance = getBalance(fromAccount);
        balance = balance.subtract(amount);
    }
    private BigDecimal getBalance(String id) { return new BigDecimal("10000.00"); }
}

Always use BigDecimal for monetary calculations in banking systems to avoid floating-point precision errors.

What are the edge cases?

Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.

JAVA
package com.bank.edge;

public class EdgeCaseDemo {
    public static void main(String[] args) {
        // Edge case 1: null handling
        String accountId = null;
        try {
            System.out.println(accountId.length());
        } catch (NullPointerException e) {
            System.out.println("Null account ID");
        }

        // Edge case 2: empty vs null
        String emptyId = "";
        System.out.println(emptyId.isEmpty());  // true
        System.out.println(emptyId == null);     // false

        // Edge case 3: boundary values
        java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
        java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
        System.out.println(zero.compareTo(negativeBalance) > 0);  // true
    }
}
TEXT
Null account ID
true
false
true

Always test with null values, empty strings, boundary values, and negative amounts in banking applications.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  Byte streams:      InputStream → byte[] → OutputStream
                     binary data, no encoding

  Character streams:  Reader → char[]/String → Writer
                     text data, with charset encoding

  Modern (Java 11+): Files.readString(path, UTF_8)
                     Files.writeString(path, text, UTF_8)

Key Takeaway

Byte streams (InputStream/OutputStream) handle raw binary data. Character streams (Reader/Writer) handle text with encoding. Use character streams for text files and byte streams for binary files. Always specify the charset explicitly and use try-with-resources.

One sentence to remember: byte streams for binary, character streams for text with encoding – always specify UTF-8 explicitly.

---

Share
Use ← → arrow keys to navigate