Java BufferedReader相关源码实例分析

 更新时间:2020年10月29日 10:59:01   作者:Y_wee  
这篇文章主要介绍了Java BufferedReader相关源码实例分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

1、案例代码

假设b.txt存储了abcdegfhijk

 public static void main(String[] args) throws IOException {
    //字符缓冲流
    BufferedReader bufferedReader=new BufferedReader(new FileReader
        (new File("H:\\ioText\\b.txt")),8);
    //存储读取的数据
    char[] charsRead=new char[5];
    //读取数据
    bufferedReader.read(charsRead);
    //遍历并输出charsRead
    for (char c:charsRead){
      System.out.println(c);
    }
  }

2、通过源码(部分)分析案例

a、第一次读取

public class BufferedReader extends Reader {
  private Reader in;//字符流
  private char cb[];//缓冲区
  private int nChars, nextChar;//nChars缓冲区可读字符数,nextChar下一个字符位置
  private static final int INVALIDATED = -2;
  private static final int UNMARKED = -1;
  private int markedChar = UNMARKED;
  private int readAheadLimit = 0; 
  private boolean skipLF = false;
  private boolean markedSkipLF = false;
  private static int defaultCharBufferSize = 8192;//缓冲区默认大小
  private static int defaultExpectedLineLength = 80;
  
  //案例调用的构造方法
   public BufferedReader(Reader in, int sz) {
     //调用父类构造
    super(in);
     //判断缓冲区大小是否正常
    if (sz <= 0)
      throw new IllegalArgumentException("Buffer size <= 0");
     //用户传入的字符流
    this.in = in;
    //给缓冲区指定空间大小(案例指定为8)
    cb = new char[sz];
     //缓冲区可读字符数和下一个字符位置初始化为0
    nextChar = nChars = 0;
  }
  
  //读取数据
  public int read(char cbuf[], int off, int len) throws IOException {
    synchronized (lock) {
      ensureOpen();
      if ((off < 0) || (off > cbuf.length) || (len < 0) ||
        ((off + len) > cbuf.length) || ((off + len) < 0)) {
        throw new IndexOutOfBoundsException();
      } else if (len == 0) {
        return 0;
      }
      //调用read1方法进行读取(真正读取数据的方法是read1方法)
      int n = read1(cbuf, off, len);
      if (n <= 0) return n;
      //将之前没处理完的数据复制到自定以数组charsRead再次调用read1方法读取
      while ((n < len) && in.ready()) {
        int n1 = read1(cbuf, off + n, len - n);
        if (n1 <= 0) break;
        n += n1;
      }
      return n;
    }
  }
  
  //cbuf用户自定义数组(charsRead),off=0,len=5
   private int read1(char[] cbuf, int off, int len) throws IOException {
    if (nextChar >= nChars) {//第一次读nextChar、nChars都为0,满足条件
      if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
        return in.read(cbuf, off, len);
      }
      //刷新缓冲区,先往下找到fill方法源码分析
      fill();
    }
    if (nextChar >= nChars) return -1;
    if (skipLF) {
      skipLF = false;
      if (cb[nextChar] == '\n') {
        nextChar++;
        if (nextChar >= nChars)
          fill();
        if (nextChar >= nChars)
          return -1;
      }
    }
    //执行完fill方法到这里,(len=5,nChars - nextChar=8-0)->n=5
    int n = Math.min(len, nChars - nextChar);
    //将缓冲区cb从nextChar开始复制n=5个字符到自定义数组
    System.arraycopy(cb, nextChar, cbuf, off, n);
    //nextChar=5
    nextChar += n;
    //n=5
    return n;
  }
  
  //刷新缓冲区方法
  private void fill() throws IOException {
    int dst;
    if (markedChar <= UNMARKED) {//markedChar初始值为UNMARKED,满足条件
      /* No mark */
      dst = 0;//初始化dst
    } else {
      /* Marked */
      int delta = nextChar - markedChar;
      if (delta >= readAheadLimit) {
        /* Gone past read-ahead limit: Invalidate mark */
        markedChar = INVALIDATED;
        readAheadLimit = 0;
        dst = 0;
      } else {
        if (readAheadLimit <= cb.length) {
          /* Shuffle in the current buffer */
          System.arraycopy(cb, markedChar, cb, 0, delta);
          markedChar = 0;
          dst = delta;
        } else {
          /* Reallocate buffer to accommodate read-ahead limit */
          char ncb[] = new char[readAheadLimit];
          System.arraycopy(cb, markedChar, ncb, 0, delta);
          cb = ncb;
          markedChar = 0;
          dst = delta;
        }
        nextChar = nChars = delta;
      }
    }
​
    int n;
    do {
      //dst=0,cb.length - dst=8-0->n=8
      n = in.read(cb, dst, cb.length - dst);
    } while (n == 0);
    if (n > 0) {//满足条件
      //nChars=8
      nChars = dst + n;
      //nextChar=0
      nextChar = dst;
    }
  }
  
}

第一次读取后charsRead存储了五个字符:abcde

b、第二次读取

//cbuf用户自定义数组(charsRead),off=0,len=5
   private int read1(char[] cbuf, int off, int len) throws IOException {
    if (nextChar >= nChars) {//第二次读nextChar=5、nChars=8,不满足条件
      if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
        return in.read(cbuf, off, len);
      }
      fill();
    }
    if (nextChar >= nChars) return -1;
    if (skipLF) {
      skipLF = false;
      if (cb[nextChar] == '\n') {
        nextChar++;
        if (nextChar >= nChars)
          fill();
        if (nextChar >= nChars)
          return -1;
      }
    }
    //跳过if直接到这里,len=5,nChars - nextChar=8-5=3->n=3
    int n = Math.min(len, nChars - nextChar);
    //将缓冲区cb从nextChar=5开始复制n=3个字符到自定义数组
    System.arraycopy(cb, nextChar, cbuf, off, n);
    //nextChar=5+3=8
    nextChar += n;
    //n=8
    return n;
  }

第二次读取只读了三个字符把charsRead五个字符的前三个覆盖:fghde

c、第三次读取

//cbuf用户自定义数组(charsRead),off=0,len=5
   private int read1(char[] cbuf, int off, int len) throws IOException {
    if (nextChar >= nChars) {//第三次读nextChar=8、nChars=8,满足条件
      if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
        return in.read(cbuf, off, len);
      }
      //刷新缓冲区,先往下找到fill方法源码分析
      fill();
    }
    if (nextChar >= nChars) return -1;
    if (skipLF) {
      skipLF = false;
      if (cb[nextChar] == '\n') {
        nextChar++;
        if (nextChar >= nChars)
          fill();
        if (nextChar >= nChars)
          return -1;
      }
    }
     //执行完fill方法到这里,(len=2,nChars - nextChar=8-0)->n=2
    int n = Math.min(len, nChars - nextChar);
    //将缓冲区cb从nextChar=0开始复制n=2个字符到自定义数组
    System.arraycopy(cb, nextChar, cbuf, off, n);
    //nextChar=5+3=8
    nextChar += n;
    //n=8
    return n;
  }
  
  //刷新缓冲区方法
  private void fill() throws IOException {
    int dst;
    if (markedChar <= UNMARKED) {//markedChar初始值为UNMARKED,满足条件
      /* No mark */
      dst = 0;//初始化dst
    } else {
      /* Marked */
      int delta = nextChar - markedChar;
      if (delta >= readAheadLimit) {
        /* Gone past read-ahead limit: Invalidate mark */
        markedChar = INVALIDATED;
        readAheadLimit = 0;
        dst = 0;
      } else {
        if (readAheadLimit <= cb.length) {
          /* Shuffle in the current buffer */
          System.arraycopy(cb, markedChar, cb, 0, delta);
          markedChar = 0;
          dst = delta;
        } else {
          /* Reallocate buffer to accommodate read-ahead limit */
          char ncb[] = new char[readAheadLimit];
          System.arraycopy(cb, markedChar, ncb, 0, delta);
          cb = ncb;
          markedChar = 0;
          dst = delta;
        }
        nextChar = nChars = delta;
      }
    }
​
    int n;
    do {
      //dst=0,cb.length - dst=8-0->n=8
      n = in.read(cb, dst, cb.length - dst);
    } while (n == 0);
    if (n > 0) {//满足条件
      //nChars=8
      nChars = dst + n;
      //nextChar=0
      nextChar = dst;
    }
  }
  
}

第三次读取了两个字符到charsRead,把最后两个字符覆盖:fghijk

3、源码执行过程图解

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 史上最简单的MyBatis动态SQL入门示例代码

    史上最简单的MyBatis动态SQL入门示例代码

    动态sql,可以根据用户对字段选择和输入,动态生成一条sql执行。接下来通过本文给大家分享MyBatis动态SQL入门示例代码,一起看看吧
    2017-03-03
  • java 中JDBC连接数据库代码和步骤详解及实例代码

    java 中JDBC连接数据库代码和步骤详解及实例代码

    这篇文章主要介绍了java 中JDBC连接数据库代码和步骤详解及实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • Spring超详细讲解事务

    Spring超详细讲解事务

    Spring事务的本质就是对数据库事务的支持,没有数据库事务,Spring是无法提供事务功能的。Spring只提供统一的事务管理接口,具体实现都是由数据库自己实现的,Spring会在事务开始时,根据当前设置的隔离级别,调整数据库的隔离级别,由此保持一致
    2022-07-07
  • 老生常谈比较排序之堆排序

    老生常谈比较排序之堆排序

    下面小编就为大家带来一篇老生常谈比较排序之堆排序。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • springBoot2.6.2自动装配之注解源码解析

    springBoot2.6.2自动装配之注解源码解析

    对于springboot个人认为它就是整合了各种组件,然后提供对应的自动装配和启动器(starter),基于这个流程去实现一个定义的装配组件,下面这篇文章主要给大家介绍了关于springBoot2.6.2自动装配之注解源码解析的相关资料,需要的朋友可以参考下
    2022-01-01
  • Java如何分析算法的时间和空间复杂度

    Java如何分析算法的时间和空间复杂度

    这篇文章主要介绍了Java如何分析算法的时间和空间复杂度,在计算机科学中,计算复杂性解释了算法的性能。文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-06-06
  • Springmvc自定义参数转换实现代码解析

    Springmvc自定义参数转换实现代码解析

    这篇文章主要介绍了Springmvc自定义参数转换实现代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 解决java web应用线上系统偶发宕机的情况

    解决java web应用线上系统偶发宕机的情况

    这篇文章主要介绍了解决java web应用线上系统偶发宕机的情况,具有好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • nacos在liunx系统中启动成功浏览器却访问不了的解决方法

    nacos在liunx系统中启动成功浏览器却访问不了的解决方法

    在linux下搭建nacos,现在想要启动,访问nacos页面,访问不了,所以本文小编将给大家介绍nacos在liunx系统中启动成功,浏览器却访问不了?全面的解决办法,需要的朋友可以参考下
    2023-09-09
  • IDEA中的clean,清除项目缓存图文教程

    IDEA中的clean,清除项目缓存图文教程

    这篇文章主要介绍了IDEA中的clean,清除项目缓存图文教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09

最新评论