天天看点

从JDK源码看InputStream

JDK 给我们提供了很多实用的输入流 xxxInputStream,而 InputStream 是所有字节输入流的抽象。包括 ByteArrayInputStream 、FilterInputStream 、BufferedInputStream 、DataInputStream 和 PushbackInputStream 等等。

InputStream 被定为 public 且 abstract 的类,实现了Closeable接口。

Closeable 接口表示 InputStream 可以被close,接口定义如下:

MAX_SKIP_BUFFER_SIZE 表示输入流每次最多能跳过的字节数。

DEFAULT_BUFFER_SIZE 默认的缓冲大小。

MAX_BUFFER_SIZE 表示最大的缓冲数组大小,这里设置为 Integer.MAX_VALUE - 8 这里也是考虑到 JVM 能支持的大小,超过这个值就会导致 OutOfMemoryError。

一共有三个 read 方法,其中有一个抽象的 read 方法,其余两个 read 方法都会调用这个抽象方法,该方法用于从输入流读取下一个字节,返回一个0到255范围的值。如果已经到达输入流结尾处而导致无可读字节则返回-1,同时,此方法为阻塞方法,解除阻塞的条件:

1. 有可读的字节。

2. 检测到已经是输入流的结尾了。

3. 抛出异常。

主要看第三个 read 方法即可,它传入的三个参数,byte数组、偏移量和数组长度。该方法主要是从输入流中读取指定长度的字节数据到字节数组中,需要注意的是这里只是尝试去读取长度为 len 的数组,但真正读取到的数组长度不一定为 len,返回值才是真正读取到的长度。

看看它的逻辑,数组为null则抛空指针,偏移量和长度超过边界也抛异常,长度为0则什么都不敢直接返回0。接着调用 read() 读取一个字节,如果为-1则说明结束,直接返回-1。否则继续根据数组长度循环调用 read() 方法读取字节,并且填充到传入的数组对象中,最后返回读取的字节数。

该方法从输入流读取所有剩余的字节,在此过程是阻塞的,直到所有剩余字节都被读取或到达流的结尾或发生异常。

逻辑是用一个 for 循环内嵌一个 while 循环,while 循环不断调用 read 方法尝试将 DEFAULT_BUFFER_SIZE 长度的字节数组填满,一旦填满则需要将数组容量扩容一倍,再将原字节数组复制到新数组中,然后再通过 while 循环继续读取,直到达到尾部才跳出 for 循环,最后返回读取到的所有字节数组。

从输入流中读取指定长度的字节,而且它能保证一定能读取到指定的长度,它属于阻塞方式,用一个 while 循环不断调用 read 读取字节,直到读取到指定长度才结束读取。

返回从该输入流能进行非阻塞读取的剩余字节数,当调用 read 读取的字节数一般会小于该值,有一些InputStream的子实现类会通过该方法返回流的剩余总字节数,但有些并不会,所以使用时要注意点。

这里抽象类直接返回0,子类中重写该方法。

从输入流中跳过指定个数字节,返回值为真正跳过的个数。这里的实现是简单通过不断调用 read 方法来实现跳过逻辑,但这是较低效的,子类可用更高效的方式重写此方法。

下面看看逻辑,最大的跳过长度不能超过 MAX_SKIP_BUFFER_SIZE ,并且用一个 while 循环调用 read 方法,如果遇到返回为-1,即已经到达结尾了,则跳出循环。可以看到 skipBuffer 其实是没有什么作用,直接让其被 GC 即可,最后返回真正跳过的字节数。

此方法用于关闭输入流,并且释放相关资源 。

从输入流中按顺序读取全部字节并且写入到指定的输出流中,返回值为转移的字节数。转移过程中可能会发生不确定次的阻塞,阻塞可能发生在 read 操作或 write 操作。

主要逻辑是用 while 循环不断调用 read 方法操作读取字节,然后调用输出流的 write 方法写入,直到读取返回-1,即达到结尾。最后返回转移的字节数。

是否支持 mark 和 reset 操作,这里直接返回 false,子类根据实际重写该方法。

标记输入流当前位置,与之对应的是 reset 方法,通过他们之间的组合能实现重复读取操作。另外它会传入 readlimit 参数,它用于表示该输入流中在执行 mark 操作后最多可以读 readlimit 个字节后才使 mark 的位置失效。

可以看到 InputStream 的 mark 方法是什么都不做的,子类中再具体实现。

与 mark 方法对应,它可以重置输入流的位置到上次被 mark 操作标识的位置。InputStream 的 reset 方法直接抛出一个 IOException,子类中根据实际情况实现。

以下是广告和相关阅读

========广告时间========

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74080321">为什么写《Tomcat内核设计剖析》</a>

=========================

相关阅读:

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73743876">从JDK源码角度看Object</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78026810">从JDK源码角度看Long</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/77196626">从JDK源码角度看Integer</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/77941129">volatile足以保证数据同步吗</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/72933108">谈谈Java基础数据类型</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51455094">从JDK源码角度看并发锁的优化</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51468764">从JDK源码角度看线程的阻塞和唤醒</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51433204">从JDK源码角度看并发竞争的超时</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51397266">从JDK源码角度看java并发线程的中断</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51371416">从JDK源码角度看Java并发的公平性</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51360228">从JDK源码角度看java并发的原子性如何保证</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78389889">从JDK源码看Writer</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78315757">从JDK源码看关闭钩子</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74557125">从JDK源码角度看Byte</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73350488">从JDK源码角度看Boolean</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/76557578">从JDK源码角度看Short</a>

欢迎关注:

从JDK源码看InputStream
从JDK源码看InputStream