Java IO流之ByteArrayInputStream分析

简介

    ByteArrayInputStream是字节数组输入流,在内存中创建了一个字节数组,将输入流中读取的数据保存到字节数组的缓存区中.也就是说字节数组输入流将读取数据放到字节数组缓冲区中.

    1.ByteArrayInputStream构造方法有两个:

public ByteArrayInputStream(byte buf[]) {}
public ByteArrayInputStream(byte buf[], int offset, int length) {}
  • 第一个构造方法是接收字节数组buf.
  • 第二个构造方法是字节数组buf,offset是偏移量,数组中读取数据的索引起始位置,length是从offset索引开始读取字节的长度.即读取的是buf数组中从offset到offset+length之间数据.

    2.内部变量

protected byte buf[];
protected int pos;
protected int mark = 0;
protected int count;
  • buf是保存字节输入流数据的字节数组.
  • pos是读取数组中的下一个字节的索引,是一个正整数,大小在0到count.
  • mark是标记的索引.可通过mark()方法和reset()方法进行设置,不设置的时候,调用第一个构造方法时值为0,调用第二个构造方法时mark值被设置成offset.
  • count是字节流的长度.当用第一个构造方法时,长度是字节数组buf的length,当用第二个构造方法时,长度是offset+length和buf.length的中的较小值.

    3.内部方法

public synchronized int read()
public synchronized int read(byte b[], int off, int len) {}
public synchronized long skip(long n) {}
public synchronized int available() {}
public boolean markSupported() {}
public void mark(int readAheadLimit) {}
public synchronized void reset() {}
public void close() throws IOException{}
  • read() : 读取下一个字节,如果已经读取完,将会返回-1.
  • read(byte b[],int off,int len) : 读取字节数组b,off位置开始,长度为len的数据.
  • skip(long n) : 跳过字节流流中n个字节.注意是跳过不是跳到.
  • available() : 表示剩余可读字节数量.
  • markSupported() : 是否支持mark()/reset().这个值总是返回true.
  • mark(int readAheadLimit) : 标记当前的位置,readAheadLimit在此处没有意义.
  • reset() : 将缓冲区的位置重置到mark标记的位置.
  • close() : 关闭流.在流已经关闭的情况,调用字节输入流中的其他方法,不会抛出异常.

 

案例

public class ByteArrayInputStreamDemo {
  public static void main(String[] args) {
    byte[] bytes = "abcdefghijklmnopqrst".getBytes();
    ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
    //avaiable表示剩余可字节数,除了a,还剩19个字节
    if(byteStream.available()>0) {
      System.out.println((char)byteStream.read()+"------剩余可读字节数="+byteStream.available());
    }
    for(int i  = 0 ; i < 3;i++) {
      if(byteStream.available()>0) {
        System.out.println((char)byteStream.read());
      }
    }
    //现在位置是d,跳过3个字节,下一个字节是h
    long skip = byteStream.skip(3);
    System.out.println((char)byteStream.read()+"-----跳过字节数="+skip);
    if(byteStream.markSupported()) {
      System.out.println("support mark");
    }
    //现在是位置在i,进行标记.
    byteStream.mark(0);
    //使用字节数组,一次性读取三个字节.
    byte[] byteArray = new byte[3];
    byteStream.read(byteArray, 0, 2);
    System.out.println(new String(byteArray));
    //通过reset()方法将指针指到到mark位置
    byteStream.reset();
    System.out.println((char)byteStream.read());
    
  }

运行结果:

a------剩余可读字节数=19
b
c
d
h-----跳过字节数=3
support mark
ij

代码分析:

(a)read()方法只能读取一个字节,读取的是下一个字节.

(b)read(buf,offset,len)方法,创建一个字节数组buf,将流里面数据读取到buf里面,位置从offset开始,长度是len.

(c)available()表示的是剩余可读的字节数,代码中当读取了a的时候,剩余的可读取的字节数量还剩19个字节.

(d)mark()将下一个的字节的位置进行标记,在读取到后面的几个字节后,可以使用reset()重新回到该位置.

(e)skip(n):表示跳过的字节数,下一个字节算起,代码中是e开始算起,跳过3个字节,到g结束.下一个读取的字节是h.

源码分析

public class ByteArrayInputStream extends InputStream {

  // 字节数组用于保存流里面的数据
  protected byte buf[];

  // 下一个会被读取的字节的索引
  protected int pos;

  // 标记的索引
  protected int mark = 0;

  // 流数据长度
  protected int count;

  // 构造函数--创建一个数据为buf字节数组的输入流
  public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    // 初始下一个读取的字节的索引是0
    this.pos = 0;
    // 当使用此构造方法时,count的大小值是buf.length
    this.count = buf.length;
  }

  // 构造函数--创建字节输入流,它的数据是buf字节数组中,从offset索引开始,读取的长度为length的数据.
  public ByteArrayInputStream(byte buf[], int offset, int length) {
    this.buf = buf;
    this.pos = offset;
    // 当使用此构造方式时,count的大小是offset+length,buf.length中较小的值
    this.count = Math.min(offset + length, buf.length);
    // 当使用此构造方法时,标记位置的默认位置是offset
    this.mark = offset;
  }

  // 读取下一个字节
  public synchronized int read() {
    return (pos < count) ? (buf[pos++] & 0xff) : -1;
  }

  // 将数据的读取到字节数组b中,off是将流数据写到数组开始的索引,len是写入长度
  public synchronized int read(byte b[], int off, int len) {
    if (b == null) {
      throw new NullPointerException();
    } else if (off < 0 || len < 0 || len > b.length - off) {
      throw new IndexOutOfBoundsException();
    }
    // 下一个读取字节的索引大于等于count,表示数据已经读完
    if (pos >= count) {
      return -1;
    }
    // len大于剩余可读的字节长度,len将会置为剩余可读字节长度
    int avail = count - pos;
    if (len > avail) {
      len = avail;
    }
    if (len <= 0) {
      return 0;
    }
    System.arraycopy(buf, pos, b, off, len);
    pos += len;
    return len;
  }

  // 跳过字节数,返回值是实际跳过的字节数量.
  public synchronized long skip(long n) {
    // 流数据长度count-下一个读取字节索引pos
    long k = count - pos;
    // 跳过字节数小于剩余可读字节数k时,跳过字节数为0,实际跳过字节数为0,是否实际是n
    if (n < k) {
      k = n < 0 ? 0 : n;
    }
    // 将pos加上实际跳过字节数.
    pos += k;
    return k;
  }

  // 可读字节数量,流数据长度count-下一个读取字节索引pos
  public synchronized int available() {
    return count - pos;
  }

  // 是否支持标记,此方法将返回的true
  public boolean markSupported() {
    return true;
  }

  // 标记当前位置。readAheadLimit在此无实际意义
  public void mark(int readAheadLimit) {
    mark = pos;
  }

  // 将位置重置到mark()方法标记的位置
  public synchronized void reset() {
    pos = mark;
  }

  // 此方法在流已经关闭的情况下,不会抛出异常
  public void close() throws IOException {}
}

源码总结:

(a)count实际上流内容即字节数组可用的索引值+1,读取时将下一个要读取索引pos与count比较,小于count时,表明流字节数据里面存在这个索引,所以可以读取到流内容.

(b)流里面数据或者内容是要看构造函数里面buf字节数组,以及offset和读取长度len,下面的read(byte[] b, int off,int len)表示的 是将流里面内容读取到字节数组中,两者要注意区分.

 

 

你可能感兴趣的:(Java,IO流)