BufferedInputStream源码逐行解析【java8】

版本:Java 8

一、进入源码的方法:

打一个类名,光标点上去,按ctrl+b:

或者ctrl + shift + a进行万能搜索,搜索这个类:

二、阅读,line by line:

默认的buf大小是8192,最大是MAX_INT - 8, 减去8的原因是一些jvm会在数组中保留一些信息,这样做可以防止内存不足;

接下来是 创建一个 AtomicReferenceFieldUpdater, 这个对象的作用和CAS参考下面,这里就不写了,简单理解就是为了修改buf【】变量的,可以不加锁保证安全:

count和pos变量:一个是保存现在buf【】里最后有效的字节的索引+1(one greater ),一个是当前的将要从buf【】中读取的下一个字节的索引。

markpos和marklimit,和标记有关,pos是标记位置,初始化为-1,marklimit是在mark和reset之间的最大距离,如果reset-mark超过了这个值,mark标记就会被丢弃:

获取自己的输入流(继承了FilterInputStream,所以有in变量),并确保打开,注意这是一个private方法:

获取一个Byte数组,指向的是自己的buf【】,并且确保不为空,也是private的,其实和上面一样就是一个Getter方法:

【重点,构造方法】:

第一个构造方法传入一个in输入流,其中调用的是第二个构造方法,第二个构造方法先调父类的构造,然后默认用DEFAULT_BUFFER_SIZE new一个byte 数组。

在super上 ctrl+b,可以进到父类FilterInputStream中,可以看到其实做的一件事就是保存一下传入的in到成员变量in中(本人喜欢说成员变量)

接下来分析 fill()方法,注意是私有的!作用是从in中读更多内容到buf【】中:

首先通过getBufIfOpen获取自己的buf【】引用,然后这些判断基本上是为了确定buffer到底应该有多大,如果空间不够,会先尝试丢弃markpos前的部分,不行会用nsz进行扩容,默认是二倍二倍地扩,最大不超过MAX_BUFFER_SIZE,扩容时修改buffer用的就是前面的cas操作对象。

核心只在于,用 in 变量read到buffer中(一次会读length-pos,从pos起),然后更新count值和pos值!

可以看看arraycopy的注释,src和dest相同也可以,会用暂时变量来确保拷贝安全的。

点到InputStream的read(byte b[] ...)方法中,可以看看具体的拷贝的实现,其实就是一次读一个byte而已,注意类型的转换,其实BufferedInputStream就是简单包装了一次这个byte b[ ],包装到了buf【】成员变量里,再加一下别的小功能罢了。

而关键方法read,做的事就是把buf【】按索引返回,并确保范围在 0 - 255 内,而且会自动调用fill(),如果没东西读了,返回 -1 。

read1()方法,先求一下avail,表示最大可读的数量,和传入的len比较,得最小的那个,进行arrcopy。

(注意,每个写方法都带syncronized,读方法不带)

接下来看skip()方法,和前面的那个一样,最多能跳过avail个元素,然后修改pos,因为read()时是从pos处开始的,增加pos就是跳过。

mark和reset,一个是标记当前位置为pos,一个是用mark标志还原pos,这样可以回去读前面的内容,因为read本质上就是读成员变量buf【】。

最后,close方法,会安全地设置buf【】为null,并关闭in流。

总结: BufferedInputStream本质上在内部分配了一个byte数组,会在read()调用时先用构造方法传入的in流把数据填充到数组中(一个循环),再从数组中读内容。 支持的额外功能就是打标记和还原pos指针。
经验分享 程序员 微信小程序 职场和发展