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、源码执行过程图解

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

(0)

相关推荐

  • 基于自定义BufferedReader中的read和readLine方法

    实例如下所示: package day0208; import java.io.FileReader; import java.io.IOException; /* * 自定义读取缓冲区,实现BufferedReader功能 * 分析: * 缓冲区就是封装了一个数组,并对外提供了更多的方法对数组进行访问 * 其实这些方法最终操作的都是数组的角标 * 缓冲的原理: * 其实就是从源中获取一批数据装进缓冲区,再从缓冲区取出数据 * 当此次取完后,继续从源中取出一批数据到缓冲区 * 当源中的数据取光时

  • Java输入流Scanner/BufferedReader使用方法示例

    1.使用Scanner 使用时需要引入包import java.util.Scanner;首先定义Scanner对象 Scanner sc = new Scanner(System.in);如果要输入整数,则 int n = sc.nextInt();String类型的,则String temp = sc.next(); 比如: 复制代码 代码如下: import java.util.Scanner; public class Test {    @SuppressWarnings("resou

  • 自定义BufferedReader的实例

    缓冲区的建立目的就是增加传输性能,使我们传输数据更加快速 缓冲区的内部其实很简单 就是在内部封装了一个数组 用数组来存储数据 对外提供一些方法对数组进行访问 其中这些方法的操作就是对数组的指针(角标). 缓冲区的原理:从源中获取一批数据到缓冲区,再从缓冲区中一个一个取出 取完后用-1作为结束标记 在BufferedReader中我们读取数据方法有read() ,readLine() read() 其参数为char[],cbuf,int off,int len.其返回值为-1 readLine()

  • Java 中的 BufferedReader 介绍_动力节点Java学院整理

    BufferedReader 介绍 BufferedReader 是缓冲字符输入流.它继承于Reader. BufferedReader 的作用是为其他字符输入流添加一些缓冲功能. BufferedReader 函数列表 BufferedReader(Reader in) BufferedReader(Reader in, int size) void close() void mark(int markLimit) boolean markSupported() int read() int

  • InputStreamReader和BufferedReader用法及实例讲解

    一.BufferedReader类 . 所属类库: java.lang.Object java.io.Reader java.io.BufferedReader . 基本概念 : public class BufferedReader    extends Reader 从字符输入流中读取文本,缓冲各个字符,从而实现字符.数组和行的高效读取. 可以指定缓冲区的大小,或者可使用默认的大小.大多数情况下,默认值足够大. 通常, Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取

  • Java BufferedWriter BufferedReader 源码分析

    一:BufferedWriter 1.类功能简介: BufferedWriter.缓存字符输出流.他的功能是为传入的底层字符输出流提供缓存功能.同样当使用底层字符输出流向目的地中写入字符或者字符数组时.每写入一次就要打开一次到目的地的连接.这样频繁的访问不断效率底下.也有可能会对存储介质造成一定的破坏.比如当我们向磁盘中不断的写入字节时.夸张一点.将一个非常大单位是G的字节数据写入到磁盘的指定文件中的.没写入一个字节就要打开一次到这个磁盘的通道.这个结果无疑是恐怖的.而当我们使用Buffered

  • Java中BufferedReader类获取输入输入字符串实例

    使用Scanner来取得使用者的输入很方便,但是它以空白来区隔每一个输入字符串,在某些时候并不适用,因为使用者可能输入一个字符串,中间会包括空白字元,而您希望取得完整的字符串. 您可以使用BufferedReader类别,它是java.io包中所提供的一个类,所以使用这个类时必须先import java.io包:使用BufferedReader对象的readLine()方法必须处理IOException异常(exception),异常处理机制是Java提供给程序设计人员捕捉程序中可能发生的错误所

  • Java中BufferedReader与BufferedWriter类的使用示例

    BufferedReader BufferedReader 是缓冲字符输入流.它继承于Reader. BufferedReader 的作用是为其他字符输入流添加一些缓冲功能. 创建BufferReader时,我们会通过它的构造函数指定某个Reader为参数.BufferReader会将该Reader中的数据分批读取,每次读取一部分到缓冲中:操作完缓冲中的这部分数据之后,再从Reader中读取下一部分的数据. 为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能

  • 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]; //读取数据 b

  • java锁机制ReentrantLock源码实例分析

    目录 一:简述 二:ReentrantLock类图 三:流程简图 四:源码分析 lock()源码分析: 非公平实现: 公平锁实现: tryAcquire()方法 公平锁实现: 非公平锁实现: addWaiter() acquireQueued() shouldParkAfterFailedAcquire() parkAndCheckInterrupt() unlock()方法源码分析: tryRelease() unparkSuccessor() 五:总结 一:简述 ReentrantLock是

  • java 中Buffer源码的分析

    java 中Buffer源码的分析 Buffer Buffer的类图如下: 除了Boolean,其他基本数据类型都有对应的Buffer,但是只有ByteBuffer才能和Channel交互.只有ByteBuffer才能产生Direct的buffer,其他数据类型的Buffer只能产生Heap类型的Buffer.ByteBuffer可以产生其他数据类型的视图Buffer,如果ByteBuffer本身是Direct的,则产生的各视图Buffer也是Direct的. Direct和Heap类型Buff

  • java集合 ArrayDeque源码详细分析

    问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的两端都可以进出元素,故而得名双端队列. ArrayDeque是一种以数组方式实现的双端队列,它是非线程安全的. 继承体系 通过继承体系可以看,ArrayDeque实现了Deque接口,Deque接口继承自Queue接口,它是对Queue的一种增强. public interface Deque<E>

  • Java从JDK源码角度对Object进行实例分析

    Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类.比如你随便创建一个classA,虽然没有明说,但默认是extendsObject的. 后面的三个点"..."表示可以接受若干不确定数量的参数.老的写法是Objectargs[]这样,但新版本的java中推荐使用...来表示.例如 publicvoidgetSomething(String...strings)(){} object是java中所有类的父类,也就是说所有的类,不管是自己创建的类还是

  • Java struts2请求源码分析案例详解

    Struts2是Struts社区和WebWork社区的共同成果,我们甚至可以说,Struts2是WebWork的升级版,他采用的正是WebWork的核心,所以,Struts2并不是一个不成熟的产品,相反,构建在WebWork基础之上的Struts2是一个运行稳定.性能优异.设计成熟的WEB框架. 我这里的struts2源码是从官网下载的一个最新的struts-2.3.15.1-src.zip,将其解压即可.里面的目录页文件非常的多,我们只需要定位到struts-2.3.15.1\src\core

  • java向文件中追加内容与读写文件内容源码实例代码

    java向文件中追加内容与读写文件内容源码实例代码 向文件尾加入内容有多种方法,常见的方法有两种: RandomAccessFile类可以实现随机访问文件的功能,可以以读写方式打开文件夹的输出流 public void seek(long pos)可以将读写指针移到文件尾,参数Pos表示从文件开头以字节为单位测量的偏移位置,在该位置文件指针. public void write(int pos)将数据写到读写指针后面,完成文件的追加.参数pos表示要写入的Byte 通过FileWrite打开文件

  • Java源码角度分析HashMap用法

    -HashMap- 优点:超级快速的查询速度,时间复杂度可以达到O(1)的数据结构非HashMap莫属.动态的可变长存储数据(相对于数组而言). 缺点:需要额外计算一次hash值,如果处理不当会占用额外的空间. -HashMap如何使用- 平时我们使用hashmap如下 Map<Integer,String> maps=new HashMap<Integer,String>(); maps.put(1, "a"); maps.put(2, "b&quo

  • Java 读写锁源码分析

    前言 在实际项目中,比如我们有一个共享资源文件,我们程序会会同时并发的去读.写这个共享资源文件,那怎么能保证在高并发场景下安全.高效读写呢?OK,看了下文便知 提示:以下是本篇文章正文内容,案例仅供参考 一.技术介绍 1.ReentranReadWriteLock是什么? ReadWriteLock提供了readLock和writeLock两种锁的操作机制,一个是读锁,一个是写锁,而它的实现类就是ReentranReadWriteLock 读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的

  • java线程池核心API源码详细分析

    目录 概述 源码分析 Executor ExecutorService ScheduledExecutorService ThreadPoolExecutor ScheduledThreadPoolExecutor 总结 概述 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中.如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有

随机推荐