ByteArrayInputStream 类源码分析

网友投稿 850 2022-10-25

ByteArrayInputStream 类源码分析

ByteArrayInputStream 类源码分析

这是《水煮 JDK 源码》系列 的第3篇文章,计划撰写100篇关于JDK源码相关的文章

ByteArrayInputStream 类位于 java.io 包下,继承于 InputStream 类,表示字节数组输入流,它会在内存中创建一个字节数组缓冲区,然后把从输入流中读取的数据全部保存在缓冲区中,其 UML 类图如下:

::: hljs-center

:::

1、成员变量

在 ByteArrayInputStream 中定义了4个成员变量,如下:

/** 存放数据的字节数组缓冲区 */ protected byte buf[]; /** 从字节数组缓冲区读取的下一个字节的位置索引 */ protected int pos; /** 当前流中标记的位置,初始化时为0 */ protected int mark = 0; /** 字节数组缓冲区中有效的字节数 */ protected int count;

2、构造方法

创建 ByteArrayInputStream 字节数组输入流主要有以下的两种方式:

public ByteArrayInputStream(byte buf[]) { this.buf = buf; // 第一个要读取的字符位置索引为0 this.pos = 0; this.count = buf.length; } public ByteArrayInputStream(byte buf[], int offset, int length) { this.buf = buf; // 第一个读取的位置索引为 offset this.pos = offset; this.count = Math.min(offset + length, buf.length); // 标记位置也为 offset this.mark = offset; }

3、读取字节方法

ByteArrayInputStream 提供了两个读取字节的方法,如下:

public synchronized int read() { // 如果下一个读取的字符位置大于字节数组的长度,直接返回 -1,说明此时数组已经读完了 // 每读取一个字节后,pos的位置就自动 +1 return (pos < count) ? (buf[pos++] & 0xff) : -1; } /** 将字节数组输入流中从 off 位置开始之后的最多 len 个长度的字节数据,读取到参数数组 b[] 中,返回的是实际读取的数据长度 */ public synchronized int read(byte b[], int off, int len) { // 如果参数数组为null,则直接抛出空指针异常 if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { // 数组越界检查 throw new IndexOutOfBoundsException(); } // 如果输入流中已经没有可读数据了,直接返回-1 if (pos >= count) { return -1; } // 输入流中可读取的数据长度 avail int avail = count - pos; // 如果希望从流中读取的数据长度 len 大于流中可读取的长度 avail, // 那么将最多从流中读取 avail 个字节数据 if (len > avail) { len = avail; } if (len <= 0) { return 0; } // 使用 System.arraycopy 方法从 buf[] 中复制字节数据到参数 b[] 中 System.arraycopy(buf, pos, b, off, len); // 读取完后,改变 pos 的位置索引 pos += len; return len; }

上面的两个 read() 方法都使用了 synchronized 关键字,即表示为同步方法,从具体的实现上可以看出,ByteArrayInputStream 中的数据是不可以重复读取的,每读取一个字节数据,下一个可读取的字节位置索引便为自动+1,如果已经读取到了字节数组的最后一个元素,则不能再读取了。

4、其他方法

ByteArrayInputStream 类提供了其他的一些方法,如下:

public synchronized long skip(long n):从输入流中跳过 n 个字节; public synchronized int available():获取还可以从输入流中读取的字节长度; public boolean markSupported():判断输入流是否支持标记功能,默认返回 true,即 ByteArrayInputStream 是支持的; public void mark(int readAheadLimit):设置输入流中的标记位置为当前可读取的位置; public synchronized void reset():重置输入流中下一个可读取位置为标记位置; public void close():关闭输入流;

下面分别看看这些方法的具体实现。

public synchronized long skip(long n) { // 获取当前输入流中可读取的数据长度 k long k = count - pos; // 判断需要跳过的长度 n 是否小于可读取的长度 k if (n < k) { k = n < 0 ? 0 : n; } // 调整下一个可读取的字节位置索引,直接偏移 k 个长度 pos += k; // 返回的是实际跳过的字节数据长度,不一定等于 n return k; } public synchronized int available() { // 获取还可以从输入流中读取的字节长度 return count - pos; } public boolean markSupported() { // 判断输入流是否支持标记功能,默认返回 true return true; } public void mark(int readAheadLimit) { // 设置输入流中的标记位置为当前可读取的位置,其中参数 readAheadLimit 没有任何用途 mark = pos; } public synchronized void reset() { // 重置输入流中下一个可读取的位置为标记位置 pos = mark; } public void close() throws IOException { // 没有任何代码实现 }

和 ByteArrayOutputStream 一样,ByteArrayInputStream 类中的 close() 方法也是没有任何代码实现的,即使被调用,也没有任何的作用。

5、示例代码

5.1 简单数据读取

上面分析了 ByteArrayInputStream 类的相关源码,下面通过一个简单的示例来看看 ByteArrayInputStream 该如何使用。

package com.magic.test; import java.io.ByteArrayInputStream; public class ByteArrayInputStreamTest { public static void main(String[] args) { byte[] bytes = "01234".getBytes(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); int c; while ((c = inputStream.read()) != -1) { System.out.println(Character.toUpperCase((char) c)); } } }

运行后,输出的结果如下:

0 1 2 3 4

5.2 验证不可重复读

下面来验证一下输入流是不可以重复读的。

package com.magic.test; import java.io.ByteArrayInputStream; public class ByteArrayInputStreamTest { public static void main(String[] args) { byte[] bytes = "01234".getBytes(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); int c; System.out.println("第1次读取"); while ((c = inputStream.read()) != -1) { System.out.println(Character.toUpperCase((char) c)); } System.out.println("第2次读取"); while ((c = inputStream.read()) != -1) { System.out.println(Character.toUpperCase((char) c)); } } }

运行后,输出结果如下:

第1次读取 0 1 2 3 4 第2次读取

5.3 可重复读取实现

从上面的结果可以看出,第2次读取时,未读取到任何数据,如果希望能够再次读取,那么又该如何处理呢?可以在第1次读取完后,使用 reset() 方法进行重置。

package com.magic.test; import java.io.ByteArrayInputStream; public class ByteArrayInputStreamTest { public static void main(String[] args) { byte[] bytes = "01234".getBytes(); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); int c; System.out.println("第1次读取"); while ((c = inputStream.read()) != -1) { System.out.println(Character.toUpperCase((char) c)); } // 使用 reset 方法进行重置,从上面对 reset() 方法源码可以看出 // 重置后 pos = mark,而 mark 的默认值为 0,此时 pos = 0 // 那么又可以从索引位置 0 开始读取字节数据了 inputStream.reset(); System.out.println("第2次读取"); while ((c = inputStream.read()) != -1) { System.out.println(Character.toUpperCase((char) c)); } } }

运行后输出结果如下:

第1次读取 0 1 2 3 4 第2次读取 0 1 2 3 4

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:IMAP API - 自托管应用程序,可通过REST访问IMAP帐户
下一篇:移动互联网进入“超级App+小程序”时代,超级App生态构建案例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~