java.io中的 ByteArrayInputStream 类可用于读取输入数组(字节)。

要创建字节数组输入流,我们必须首先导入java.io.ByteArrayInputStream包。导入包后,我们有两个构造函数可用于创建输入流:


ByteArrayInputStream input = new ByteArrayInputStream(arr);
ByteArrayInputStream input = new ByteArrayInputStream(arr, 2, 2);
    

类里面有4个字段:


// Byte array provided by the creator of the stream
protected byte buf[];

// Index of the next character to read from the input stream's buffer
protected int pos;

// Current marked position in the stream
protected int mark = 0;

// Index is one greater than the last valid character in the input stream's buffer
protected int count;
    

这是我们的构造函数:


public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

public ByteArrayInputStream(byte buf[], int offset, int length) {
    this.buf = buf;
    this.pos = offset;
    this.count = Math.min(offset + length, buf.length);
    this.mark = offset;
}
    

ByteArrayInputStream 类的方法

方法 行动
内部读取() 从此输入流中读取下一个字节的数据。
int read(byte b[], int off, int len) 从输入流中读取几个字节并将它们存储在缓冲区数组b中。
off是目标数组b的偏移量。
len是要读取的最大字节数。
长跳过(长n) 跳过此输入流中的 n 个字节的输入。返回跳过的字节数(如果我们到达输入流的末尾,它可能小于 n)。
内部可用() 返回可以从此输入流中读取(或跳过)的剩余字节数。
无效重置() 将缓冲区重置为标记位置。标记的位置为 0,除非标记了另一个位置或在构造函数中指定了不同的偏移量。
布尔标记支持() 检查此InputStream是否支持标记/重置。ByteArrayInputStream返回true
无效关闭() 什么都不做。
无效标记(int readAheadLimit) 设置标记等于当前位置的字段。如果调用了reset方法,那么后续的读取将从该位置开始。readAheadLimit参数未使用且不影响该方法的行为。

让我们仔细看看这些方法,看看它们在实践中是如何工作的。

读()

当您想像从普通InputStream读取字节一样从ByteArrayInputStream读取字节时,可以使用read()方法。


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       for (int i = 0; i < array.length; i++) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

可用的()

如果你想检查你的缓冲区中是否有东西,你可以调用available()方法。


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       System.out.println("Bytes available for reading: " + input.available());

       input.read();
       System.out.println("Bytes available for reading " + input.available());

       input.read();
       System.out.println("Bytes available for reading " + input.available());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

我们将看到每次从缓冲区读取后可用于读取的字节数都会发生变化。

输出:

可供读取的字节数:4
可供读取的字节数:3
可供读取的字节数:2

跳过(长n)

您可以使用skip()方法跳过一定数量的字节而不读取它们。


public static void main(String[] args) {
   byte[] array = {1, 2, 3, 4};

   try (ByteArrayInputStream input = new ByteArrayInputStream(array)) {
       input.skip(2);

       while (input.available() != 0) {
           int data = input.read();
           System.out.print(data + ", ");
       }
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

输出:

3, 4,

重置()

此方法将缓冲流的位置重置为最后标记的位置。除非设置不同的标记,否则它是位置 0。


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

       System.out.println("Calling reset() method");
       input.reset();
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

我们将看到调用reset()方法会将我们带到流的起点。

输出:

读取:65
读取:66
读取:67
读取:68
调用 reset() 方法
读取:65
读取:66

标记(int readAheadLimit)

ByteArrayInputStream类的mark ()方法在当前字节位置设置内部标记,即紧接在先前读取的字节之后。此方法采用一个参数,该参数指示在标记之后流变为无效之前可以读取多少字节。默认情况下,如果未显式设置标记,则ByteArrayInputStream会标记位置 0 或传递给其构造函数的偏移量的位置。重要的是要注意readAheadLimit标记与此类无关。


/* Note: For this class, {@code readAheadLimit}
*  has no meaning.
*
* @since   1.1
*/
public void mark(int readAheadLimit) {
   mark = pos;
}
    

下面是使用mark()reset()方法在ByteArrayInputStream中设置标记的示例。我们将在前面的示例中添加对mark()方法的调用:


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream input = new ByteArrayInputStream(buf)) {
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());
       input.mark(5);

       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

       System.out.println("Calling reset() method");
       input.reset();

       System.out.println("Read: " + input.read());
       System.out.println("Read: " + input.read());

   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

我们可以看到当前流的位置发生了变化。

输出:

读取:65
读取:66
读取:67
读取:68
读取:69
调用 reset() 方法
读取:68
读取:69

标记支持()

markSupported ()方法让您检查是否可以设置标记。要了解返回值的来源,让我们转到方法的代码:


/**
* Tests if this {@code InputStream} supports mark/reset. The
* {@code markSupported} method of {@code ByteArrayInputStream}
* always returns {@code true}.
*
* @since   1.1
*/
public boolean markSupported() {
   return true;
}
    

该方法始终返回true。让我们在实践中对此进行测试。


public static void main(String[] args) {
   byte[] buf = {65, 66, 67, 68, 69};
   try (ByteArrayInputStream bais = new ByteArrayInputStream(buf)) {
       boolean isMarkSupported = bais.markSupported();

       System.out.println("isMarkSupported: " + isMarkSupported);
       System.out.println("Read: " + bais.read());
       System.out.println("Read: " + bais.read());

       bais.mark(1);
       System.out.println("Read: " + bais.read());
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);

       bais.reset();
       isMarkSupported = bais.markSupported();
       System.out.println("isMarkSupported: " + isMarkSupported);
   } catch (IOException e) {
       e.printStackTrace();
   }
}
    

在执行了mark()reset()方法之后,我们的流就准备好了并且支持标记:

输出:

isMarkSupported:true
读取:65
读取:66
读取:67
isMarkSupported:true
isMarkSupported:true

关闭()

为了理解close方法,让我们也看一下它的内部:


/**
* Closing a {@code ByteArrayInputStream} has no effect. The methods in
* this class can be called after the stream has been closed without
* generating an {@code IOException}.
*/
public void close() throws IOException {
}
    

close 方法的文档告诉我们关闭 ByteArrayInputStream没有任何效果。可以在流关闭后调用ByteArrayInputStream类方法而不抛出IOException

我们能得出什么结论?

当我们想从字节数组中读取数据时,我们需要一个ByteArrayInputStream 。将此类与知道如何使用InputStreams的其他代码结合使用而不是单独使用此类通常是有意义的。