Fundamentals 15 min read

Java IO Streams: Byte, Character, and Conversion Streams with Performance Benchmarks

This tutorial covers Java's three main IO stream types—byte, character, and conversion streams—demonstrating low-level vs high-level stream performance with file copy benchmarks, text file processing, console input handling, and character encoding conversion solutions.

Java Captain
Java Captain
Java Captain
Java IO Streams: Byte, Character, and Conversion Streams with Performance Benchmarks

Byte Streams

Java byte streams are built on two abstract base classes: InputStream for input and OutputStream for output. The article clarifies that Input means data moves into memory (read), while Output means data moves out of memory (write).

Byte streams are divided into low-level streams ( FileInputStream, FileOutputStream) and high-level buffered streams ( BufferedInputStream, BufferedOutputStream). Low-level streams are rarely used directly; they mainly serve as constructor arguments for high-level streams, which provide convenient methods and greatly improve read/write efficiency.

Performance Comparison: File Copy Benchmark

A 1.13 MB AVI file is copied using four approaches, with execution times measured via System.currentTimeMillis():

Low-level stream, byte-by-byte: 13,394 ms

Low-level stream, 1 KB byte array buffer: 29 ms

High-level buffered stream, byte-by-byte: 12 ms

High-level buffered stream, 1 KB byte array buffer: 3 ms

Even when reading byte-by-byte, the buffered stream outperforms the low-level stream with a manual array buffer because the buffered stream internally uses an array buffer. Adding an explicit array buffer on top of the buffered stream yields the best result (3 ms).

Code uses try-with-resources (available since JDK 8) for automatic resource closure, similar to C#'s using keyword.

import java.io.*;

public class ByteIOTest {
    public static void main(String[] args) {
        // Low-level byte-by-byte
        long start1 = System.currentTimeMillis();
        try (FileInputStream fis = new FileInputStream("D:\\天蓬风云录.avi");
             FileOutputStream fos = new FileOutputStream("D:\\宫廷内斗史01.avi")) {
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        } catch (IOException e) { e.printStackTrace(); }
        long end1 = System.currentTimeMillis();
        System.out.println("低级流【逐个字节】复制文件耗时:" + (end1 - start1) + "毫秒");

        // Low-level with byte array
        long start2 = System.currentTimeMillis();
        try (FileInputStream fis = new FileInputStream("D:\\天蓬风云录.avi");
             FileOutputStream fos = new FileOutputStream("D:\\宫廷内斗史02.avi")) {
            byte[] bArr = new byte[1024];
            int len;
            while ((len = fis.read(bArr)) != -1) {
                fos.write(bArr, 0, len);
            }
        } catch (IOException e) { e.printStackTrace(); }
        long end2 = System.currentTimeMillis();
        System.out.println("低级流【采用数组】复制文件耗时:" + (end2 - start2) + "毫秒");

        // High-level byte-by-byte
        long start3 = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\天蓬风云录.avi"));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\宫廷内斗史03.avi"))) {
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) { e.printStackTrace(); }
        long end3 = System.currentTimeMillis();
        System.out.println("高级流【逐个字节】复制时间:" + (end3 - start3) + "毫秒");

        // High-level with byte array
        long start4 = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\天蓬风云录.avi"));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\宫廷内斗史04.avi"))) {
            byte[] bArr = new byte[1024];
            int len;
            while ((len = bis.read(bArr)) != -1) {
                bos.write(bArr, 0, len);
            }
        } catch (IOException e) { e.printStackTrace(); }
        long end4 = System.currentTimeMillis();
        System.out.println("高级流【采用数组】复制时间:" + (end4 - start4) + "毫秒");
    }
}
/*
Output:
低级流【逐个字节】复制文件耗时:13394毫秒
低级流【采用数组】复制文件耗时:29毫秒
高级流【逐个字节】复制时间:12毫秒
高级流【采用数组】复制时间:3毫秒
*/

Character Streams

Character streams handle text and involve character sets (GBK, UTF-8, etc.). The abstract base classes are Reader (input) and Writer (output). Low-level streams: FileReader, FileWriter. High-level buffered streams: BufferedReader, BufferedWriter.

Key methods: BufferedReader.readLine() — reads a line without line terminators; returns null at EOF. BufferedWriter.newLine() — writes a platform-specific line separator ( on Windows, on Linux, on macOS).

Text File Copy Example

import java.io.*;

public class CharIOTest {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("d:\\天蓬风云录.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\天蓬宫廷历险记.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (Exception e) { e.printStackTrace(); }
    }
}

Console Input: BufferedReader vs Scanner

The article recommends BufferedReader over Scanner for console input because it is more efficient and more powerful. Two equivalent examples show reading name and age from console and writing to a file.

// Using Scanner
try (Scanner sc = new Scanner(System.in);
     BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\人员信息01.txt"))) {
    System.out.println("请录入姓名:");
    String username = sc.next();
    System.out.println("请录入年龄:");
    String userage = sc.next();
    bw.write(username);
    bw.newLine();
    bw.write(userage);
} catch (IOException e) { e.printStackTrace(); }

// Using BufferedReader
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
     BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\人员信息02.txt"))) {
    System.out.println("请输入你的姓名:");
    String name = br.readLine();
    System.out.println("请输入你的年龄:");
    String age = br.readLine();
    bw.write(name);
    bw.newLine();
    bw.write(age);
} catch (Exception e) { e.printStackTrace(); }

Conversion Streams (Character Encoding)

Conversion streams bridge byte streams and character streams with explicit character encoding: InputStreamReader — reads bytes and decodes to characters using a specified charset. OutputStreamWriter — encodes characters to bytes using a specified charset.

Constructors allow default or explicit charset (e.g., new InputStreamReader(in, "gbk"), new OutputStreamWriter(out, "gb2312")). Charset names are case-insensitive.

Encoding Mismatch Problem and Solution

Scenario: A text file aaa.txt is saved in ANSI (GBK on Chinese Windows). The IDE defaults to UTF-8. Reading aaa.txt with UTF-8 produces garbled characters; writing that garbled data to bbb.txt yields a permanently corrupted file regardless of later encoding changes.

Solution: Read the source file with the correct encoding (GBK) using InputStreamReader. The output encoding can be chosen freely (e.g., GB2312) because the characters are already correctly decoded in memory.

import java.io.*;

public class CharIOTest {
    public static void main(String[] args) {
        // Wrong: UTF-8 read GBK file -> garbled
        /*
        try (BufferedReader br = new BufferedReader(new FileReader("d:\\aaa.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\bbb.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (Exception e) { e.printStackTrace(); }
        */

        // Correct: GBK read, GB2312 write
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(new FileInputStream("d:\\aaa.txt"), "gbk"));
             BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream("d:\\bbb.txt"), "gb2312"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (Exception e) { e.printStackTrace(); }
        // Output encoding can be anything as long as input encoding is correct.
    }
}

Summary

For text files, prefer character streams and conversion streams. For binary files (audio, video, images), use byte streams — byte streams are universal and can handle any data.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaFile I/OCharacter EncodingIO StreamsBuffered StreamsByte StreamsCharacter StreamsConversion Streams
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.