Java IO - ByteArrayInputStream&ByteArrayOutputStream

基本概念

  • ByteArrayInputStream:字节数组输入流,继承自 InputStream。它会在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。

  • ByteArrayOutputStream:字节数组输出流,继承自OutputStream是与 ByteArrayInputStream 相对应的输出流。


实例探究

1.ByteArrayInputStream

下面来看一个完整的实例。

public class Test {

// 英文字母
private static final byte[] ArrayLetters = { 
    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 
    0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
    0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 
    0x76, 0x77, 0x78, 0x79,0x7A };

private static final int LEN = 5;

public static void main(String[] args) throws IOException {
    read();
}

private static void read() {

    ByteArrayInputStream bis = new ByteArrayInputStream(ArrayLetters);

    // 逐个读取字节,输出 a,b,c,d,e
    for (int i = 0; i < LEN; i++) {
        if (bis.available() != -1) {
            int count = bis.read();
            System.out.print((char) count);
        }
    }
    System.out.println();

    // 判断是否支持标记操作
    if (!bis.markSupported()) {
        return;
    }

    // 标记当前字节,即 f(与 reset 配套使用)
    bis.mark(0);

    // 跳过5个字节,跳过 f,g,h,i,j
    bis.skip(5);

    //按照字节数组读取,输出结果 k,l,m,n,o
    byte[] buf = new byte[LEN];
    bis.read(buf, 0, LEN);
    System.out.print(new String(buf));
    System.out.println();

    // 重置字节流,回到 mark 的位置,重新读取字符数组,输出 f,g,h,i,j
    bis.reset();
    bis.read(buf, 0, LEN);
    System.out.print(new String(buf));
}

2.ByteArrayOutputStream

public static void main(String[] args) throws IOException {
    write();
}

private static void write() throws IOException {

    ByteArrayOutputStream bos = new ByteArrayOutputStream(ArrayLetters.length);

    // 按字节写入,输出 a
    bos.write(0x61);
    System.out.println(bos.toString());

    // 按字节数组写入,输出 a,b,c,d
    bos.write(ArrayLetters, 1, 3);
    System.out.println(bos.toString());

    // 缓冲数组中的字节数量,输出 4
    int size = bos.size();
    System.out.println(size);

    // 转换成字节数组,输出 a,b,c,d
    byte[] buf = bos.toByteArray();
    System.out.println(new String(bos.toByteArray()));

    //将内容输出到其他流
    bos.writeTo(new ByteArrayOutputStream(size));

}

源码分析

1.ByteArrayInputStream

首先来看它的类结构,如下图所示。

这里写图片描述

通过类结构图,我们知道它定义了4个成员变量。

//缓冲数组
protected byte buf[];

//索引位置,表示当前读取的位置
protected int pos;

//标记位,标记当前读取的位置
protected int mark = 0;

//要读取的字节数组的长度
protected int count;

成员变量的具体意义结合下面的构造函数就能一目了然。

 public ByteArrayInputStream(byte buf[]) {
      this.buf = buf;
      this.pos = 0;
      this.count = buf.length;
  }

  public ByteArrayInputStream(byte buf[], int offset, int length) {
      this.buf = buf;
      this.pos = offset;
      this.count = Math.min(offset + length, buf.length);
      this.mark = offset;
  }

接下来着重来看它的 read 方法。

//按字节读取
public synchronized int read() {

    //判断索引位置,并取出数组上相应位置的值。读取完毕返回 -1。
    return (pos < count) ? (buf[pos++] & 0xff) : -1;
}

//按字节数组读取
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();
    }

    //判断缓冲数组的字节是否已经全部读取
    if (pos >= count) {
        return -1;
    }

    //判断要读取的字节数量是否超出了缓冲数组中剩余的字节数量
    if (pos + len > count) {
        len = count - pos;
    }

    if (len <= 0) {
        return 0;
    }

    //重点-->将缓冲数组上的字节(从索引位置开始)复制到当前数组中
    System.arraycopy(buf, pos, b, off, len);

    pos += len;

    return len;
}

最后再来看看类中剩余的几个方法。

//表示要跳过的字节,通过改变数组的索引位置实现
public synchronized long skip(long n) {
     if (pos + n > count) {
         n = count - pos;
     }
     if (n < 0) {
         return 0;
     }
     pos += n;
     return n;
 }

//返回缓冲数组中剩余可读取的字节数量
public synchronized int available() {
    return count - pos;
}

//表示字节数入流默认支持标记
public boolean markSupported() {
    return true;
}

//标记字节
public void mark(int readAheadLimit) {
    mark = pos;
}

//释放标记的字节
public synchronized void reset() {
    pos = mark;
}

//关闭流,是个空方法,说明缓冲数组输入流不需要关闭流操作
public void close() throws IOException {
}

2.ByteArrayOutputStream

首先来看它的类结构,如下图所示。

这里写图片描述

通过类结构图,我们知道它定义了2个成员变量,意义与 ByteArrayInputStream 中的成员变量一样,这里不再阐述。接下来看它的构造方法。

public ByteArrayOutputStream() {
    this(32);
}

public ByteArrayOutputStream(int size) {
    if (size < 0) {
        throw new IllegalArgumentException("Negative initial size: " + size);
    }
    buf = new byte[size];
}

观察代码发现 ByteArrayOutputStream 的操作跟 ByteArrayInputStream 一样,也是通过缓冲数组完成。如果不指定缓冲数组的大小,默认为32。再来着重看看它的 write 方法。

//按字节写入 
public synchronized void write(int b) {

    int newcount = count + 1;

    //判断下一次写入时长度如果超出缓冲数组的容量,则创建新的缓冲数组并将内容复制进入
    if (newcount > buf.length) {
        buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
    }

    //关键-->写入操作
    buf[count] = (byte) b;

    count = newcount;
}

//按字节数组写入
public synchronized void write(byte b[], int off, int len) {
    if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return;
    }

    int newcount = count + len;

    if (newcount > buf.length) {
        buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
    }

    //关键-->写入操作
    System.arraycopy(b, off, buf, count, len);

    count = newcount;
}

最后再来看看它的其他方法。

//重点 -->将内容输出到其他流
public synchronized void writeTo(OutputStream out) throws IOException {
     out.write(buf, 0, count);
 }

public synchronized void reset() {
    count = 0;
}

//转化成字节数组
public synchronized byte toByteArray()[] {
    return Arrays.copyOf(buf, count);
}

public synchronized int size() {
    return count;
}

public synchronized String toString() {
    return new String(buf, 0, count);
}

public synchronized String toString(String charsetName) throws UnsupportedEncodingException {
    return new String(buf, 0, count, charsetName);
}

public synchronized String toString(int hibyte) {
    return new String(buf, hibyte, 0, count);
}

//同字节数组输入流一样,不用关闭流
public void close() throws IOException {
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oxf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值