BufferedReader源码笔记

本文详细解析了BufferedReader的源码,重点探讨了mark和reset方法的实现原理,以及如何处理标记和回滚长度限制。通过源码分析,揭示了BufferedReader如何通过内部缓冲区提高读取效率,以及在不同情况下的数据读取策略。

       怎么说呢,由于看人家源码的时候发现经常会使用BufferedReader的mark(int readAheadLimit)和reset()方法,但是自己使用的时候其实经常会出现这样或者那样的问题,所以干脆就看了源码,没有注释的源码猜人家的意思的确还是很麻烦,也许是我jdk1.1的源码看的不够多,或者本身能力有限,所以花了很长时间才完全弄明白BufferedReader的源码,并且正好也复习了下设计模式之-装饰模式,点此穿越.

 

       当然这篇文章的由来是由于BufferedReader之mark与reset初探这篇文章的启发下采取看源码,推荐。

 

       从jdk帮助文档里面公布出来的BufferedReader的方法也不多,详细见下:

       1.mark(int readAheadLimit):注意这个方法标记的是当前读取的下一个字符串,readAheadLimit并不是标记字符的索引,而是可以回滚的长度限制。

       2.markSupported(): 源代码写死了, return true.

       3.read():读取单个字符

       4.read(char[] cbuf,int off,int len):将字符读入数组的某一个部分。

       5.readLine():读取一个文本行

       6.reset():重置,配合mark(int readAheadLimit)使用

       7.skip(long n):跳过字符

 

       BufferedReader的作用:

       从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

       可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。 

 

       BufferedReader为了实现缓冲各个字符的功能,在BufferedReader的构造器中会分配一个指定长度的内存空间给byte[] cb,然后要读取的字符都会事先存放在这个cb[]缓冲区域中,便于高效读取使用。那么BufferedReader是如何维护这个cb[]的呢?那要仔细研究下BufferedReader类的一个私有方法fill().代码见下,已加了中文注释.

      

 

       伪代码流程是:

       1.判断BufferedReader对象中的缓冲流的数据是否有被标记

       2.如果没有被标记转向第8步

       3.如果被标记,计算下一个读取字符索引与标记字符索引的差值delta,这个差值就是当调用reset的时候回滚的长度

       4.判断delta是否超过了mark(int readAheadLimit)中设置的readAheadLimit这个最大能回滚长度的值了,超过了则将标记置为无效,转向到第8步

       5.判断指定的readAheadLimit的值是否有超过缓冲区长度sb.length,没有超过转到第六步,超过了转到第七步

       6.将标记的字符的索引开始的长度为delta的字符保留,并且把他们设置在cb的最前面

       7.分配一个长度为readAheadLimit的缓冲区,然后将标记的字符的索引开始的长度为delta的字符保留,并且把他们设置在新缓冲区的最前面,然后将新缓冲区赋给cb

       8.如果没有被标记或者标记为无效的情况下,那么cb里面的数据都是无效的,所以从in里面获取的数据从cb的开始位置进行存放,如果被标记并且标记为有效的情况下,那么经过第6步或者第7步已经将那些有必要的数据存放到了cb的前面,从in里面或者的数据只能存放在这个必要数据的后面。而dst就是控制这个从cb那个位置存放新数据的变量。

       9.查看从in里面获取到的新数据的长度,如果是-1表示数据源已经读取完毕了,否则设置nchars的长度,nchars其实就是cb里面有效数据的长度.

       10.结束.

 

 

  PS:在写这个blog的时候,我突然想到,如果mark中的readAheadLimit这个参数不控制回滚长度,也就是fill()方法中没有

       

      这个判断语句(你可以将源码代码复制出来后注释掉这个判断然后再运行下面的语句),那我们假设一个场景

       

       这段语句会造成死循环,因为标记的缓冲区最前面那个,而且回滚的长度正好是缓冲区的长度,所以在伪代码流程的第6步中,其实cb已经没有空余的空间去获取新的数据,所以n = in.read(cb, dst, cb.length - dst);必然为0,结果就死循环了。所以个人认为readAheadLimit这个值其实最重要的功能就是保证在调用fill()这个方法的时候,缓存区有剩余的空间去获取新的内容,如果你非要使你的回滚长度等同于缓冲区长度,那么readAheadLimit的作用就变成了把缓冲区的长度扩充,以便有新的空间去容纳新的数据,否则即使没有死循环你也读取不到新的内容了。

 

 

 

另外对于

    do {

      //dst=0表示cb[]字段全部更新,dst>0表示标记字符串的长度.

      n = in.read(cb, dst, cb.length - dst);

    } while (n == 0);

暂时不明白,至少我在看BufferedReader装饰StringReader这个类的时候没看到效果。

 

BufferedReader的源码所有中文注释如下(这是我的笔记~):

 

 

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值