ZetCode

Java ByteArrayInputStream 类

最后修改时间:2025 年 4 月 16 日

java.io.ByteArrayInputStream 类允许从字节数组中读取字节,将其作为输入流。 它是 Java I/O 包的一部分,并提供了一个内存中的流实现。 当使用字节数组作为流时,此类非常有用。

ByteArrayInputStream 扩展了 InputStream 并封装了一个字节数组。 它维护一个内部位置计数器来跟踪读取进度。 可以重置流以重新读取数据。 与文件流不同,它不需要外部资源。

ByteArrayInputStream 类概述

ByteArrayInputStream 为字节数组提供流功能。 主要方法包括标准读取操作、标记/重置和流跳过。 该类不需要关闭,但为了兼容性而实现了 Closeable。

public class ByteArrayInputStream extends InputStream {
    public ByteArrayInputStream(byte[] buf);
    public ByteArrayInputStream(byte[] buf, int offset, int length);
    public synchronized int read();
    public synchronized int read(byte[] b, int off, int len);
    public synchronized long skip(long n);
    public synchronized int available();
    public synchronized void mark(int readAheadLimit);
    public synchronized void reset();
    public boolean markSupported();
    public void close();
}

上面的代码显示了 ByteArrayInputStream 的关键方法。 该类支持所有标准的 InputStream 操作。 标记/重置功能始终可用。 缓冲区不会被复制,而是被流引用。

创建 ByteArrayInputStream

通过将字节数组传递给其构造函数来创建 ByteArrayInputStream。 您可以指定整个数组或一部分。 流在原始数组上运行 - 对数组的更改会影响流。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        byte[] data = {65, 66, 67, 68, 69, 70}; // A-F in ASCII
        
        // Create stream from full array
        ByteArrayInputStream stream1 = new ByteArrayInputStream(data);
        
        // Create stream from portion (bytes 2-4)
        ByteArrayInputStream stream2 = 
            new ByteArrayInputStream(data, 2, 3);
        
        try {
            System.out.println("Stream1 contents:");
            while (stream1.available() > 0) {
                System.out.print((char) stream1.read() + " ");
            }
            
            System.out.println("\n\nStream2 contents:");
            while (stream2.available() > 0) {
                System.out.print((char) stream2.read() + " ");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了创建 ByteArrayInputStream 的两种方法。 第一个使用整个数组,而第二个使用一部分。 输出显示了不同的内容。 请注意,IOException 的可能性很小,但必须处理。

读取单个字节

从 ByteArrayInputStream 读取的最简单方法是逐字节读取。 read 方法在流的末尾返回 -1。 每次调用都会前进内部位置。 这种方法很简单,但不适用于大型数据。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        String text = "Hello ByteArrayInputStream";
        byte[] bytes = text.getBytes();
        
        try (ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            int byteData;
            System.out.println("Reading bytes:");
            
            while ((byteData = stream.read()) != -1) {
                System.out.print((char) byteData + "(" + byteData + ") ");
            }
            
            System.out.println("\n\nReading complete");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例从 ByteArrayInputStream 中以字节形式读取一个字符串。 每个字节都以字符和数值形式打印。 try-with-resources 确保正确关闭流。 虽然 ByteArrayInputStream 不需要,但这是一个好习惯。

将字节读入数组

为了获得更好的性能,一次读取多个字节到一个字节数组中。 这减少了方法调用,效率更高。 read 方法返回实际读取的字节数。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        String text = "Reading bytes in bulk is more efficient";
        byte[] source = text.getBytes();
        
        try (ByteArrayInputStream stream = new ByteArrayInputStream(source)) {
            byte[] buffer = new byte[10];
            int bytesRead;
            
            System.out.println("Reading in chunks:");
            while ((bytesRead = stream.read(buffer)) != -1) {
                String chunk = new String(buffer, 0, bytesRead);
                System.out.println("Read " + bytesRead + " bytes: " + chunk);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了批量读取到 10 字节的缓冲区中。 String 构造函数将每个块转换为文本。 bytesRead 值确保仅处理实际数据。 对于大型数据集,这种方法要快得多。

Mark 和 Reset 功能

ByteArrayInputStream 完全支持标记和重置操作。 mark 方法记住当前位置。 Reset 返回到此位置,允许重新读取数据。 readAheadLimit 参数被忽略。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        
        try (ByteArrayInputStream stream = new ByteArrayInputStream(data)) {
            // Read first 3 bytes
            System.out.println("First 3 bytes:");
            for (int i = 0; i < 3; i++) {
                System.out.print(stream.read() + " ");
            }
            
            // Mark current position
            stream.mark(100); // Parameter is ignored
            
            // Read next 3 bytes
            System.out.println("\n\nNext 3 bytes:");
            for (int i = 0; i < 3; i++) {
                System.out.print(stream.read() + " ");
            }
            
            // Reset to mark
            stream.reset();
            
            // Read again from mark
            System.out.println("\n\nAfter reset, next 3 bytes:");
            for (int i = 0; i < 3; i++) {
                System.out.print(stream.read() + " ");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了标记/重置功能。 读取 3 个字节后,我们标记该位置。 读取 3 个字节后,reset 返回到标记。 readAheadLimit 参数在 ByteArrayInputStream 中被忽略。

跳过流中的字节

skip 方法在不读取的情况下将流位置向前移动。 它返回实际跳过的字节数。 这比读取和丢弃数据更有效。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        String text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        byte[] bytes = text.getBytes();
        
        try (ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) {
            System.out.println("Initial position: " + stream.available());
            
            // Skip first 5 bytes
            long skipped = stream.skip(5);
            System.out.println("Skipped " + skipped + " bytes");
            System.out.println("Next byte: " + (char) stream.read());
            
            // Try to skip beyond end
            skipped = stream.skip(20);
            System.out.println("Skipped " + skipped + " bytes (end approached)");
            System.out.println("Remaining: " + stream.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了跳过字节。 第一个 skip 移动到前 5 个字母之后。 第二个尝试跳过 20 个字节,但仅跳过剩余的字节。 available 方法显示了剩余要读取的字节。

可用字节和位置

available 方法返回流中剩余的字节数。 这对于检查流状态很有用。 ByteArrayInputStream 还通过此方法提供位置跟踪。

Main.java
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        byte[] data = new byte[100];
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) i;
        }
        
        try (ByteArrayInputStream stream = new ByteArrayInputStream(data)) {
            System.out.println("Initial available: " + stream.available());
            
            // Read 10 bytes
            byte[] buffer = new byte[10];
            stream.read(buffer);
            System.out.println("After reading 10 bytes: " + stream.available());
            
            // Skip 20 bytes
            stream.skip(20);
            System.out.println("After skipping 20 bytes: " + stream.available());
            
            // Read remaining
            while (stream.available() > 0) {
                stream.read(buffer);
                System.out.println("Read 10 bytes, remaining: " + stream.available());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例使用 available 跟踪流位置。 每次操作后,剩余字节数都会减少。 该方法为 ByteArrayInputStream 提供精确计数。 这与其他流不同,其他流中的 available 是一个估计值。

来源

Java ByteArrayInputStream 类文档

在本文中,我们介绍了 Java ByteArrayInputStream 类的基本方法和特性。 理解这些概念对于在 Java 应用程序中使用内存中的字节数据作为流至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的程序员。 我于 2007 年开始撰写编程文章,至今已撰写了超过 1400 篇文章和八本电子书。 我拥有超过八年的教学经验,致力于分享我的知识,帮助他人掌握编程概念。

列出所有Java教程