【Java8源码分析】IO包-InputStream、FileInputStream和BufferedInputStream总结

转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72875258


Java的IO类型可分为两大类:

  • 面向字符流:Reader & Writer
  • 面向字节流:InputStream & OutputStream

本文主要从源码方面分析一下常用的面向字节流的输入流

  • InputStream:所有输入流的抽象父类,定义了常用输入函数
  • FileInputStream:针对文件的输入流
  • BufferedInputStream:带有缓冲区的高效输入流

1 InputStream源码解析

public abstract class InputStream implements Closeable {

    private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    // 读取下一个字节,返回0-255,读取失败返回-1,read()会阻塞直至读取成功或结束
    public abstract int read() throws IOException;

    // 把字节流读入到传入的字节数组
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    // 把字节读到字节数组指定位置
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            // 循环调用read,一个字节一个字节读,有可能效率非常低
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
}

InputStream默认的read(byte[])方法是通过多次调用read()函数一个字节一个字节读到字节数组中。对于文件读取这种,read()函数都要进行一次磁盘IO,所以会导致性能低下。


2 FileInputStream源码解析

public class FileInputStream extends InputStream
{
    private native int read0() throws IOException;

    private native int readBytes(byte b[], int off, int len) throws IOException;

    public int read() throws IOException {
        return read0();
    }

    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }
}

FileInputStream中的read()read(byte[])方法都是navtice方法。

InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),可想而知,如果数据量巨大,这样的磁盘消耗非常可怕。

InputStream类的read(byte[])方法则是一次读取多个字节到buffer中。


3 BufferedInputStream源码解析

3.1 属性域

public class BufferedInputStream extends FilterInputStream {

    // 存储的输入流对象
    protected volatile InputStream in;

    // 缓冲区大小默认未8MB
    private static int DEFAULT_BUFFER_SIZE = 8192;

    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

    // 缓冲区
    protected volatile byte buf[];

    // 缓冲区中有效数据容量
    protected int count;

    // 缓冲区读到的当前位置
    protected int pos;

    // 构造函数,默认缓冲区大小
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    // 构造函数,设置缓冲区大小
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    } 
}

3.2 read()函数

public synchronized int read() throws IOException {
    // 如果缓冲区已经读完,则调用fill填充缓冲区
    if (pos >= count) {
        fill();
        if (pos >= count)
            return -1;
    }
    return getBufIfOpen()[pos++] & 0xff;
}

//填充字节到缓冲区中,假设缓冲区中的数据已经全部读完
private void fill() throws IOException {
    byte[] buffer = getBufIfOpen();
    // ...
    // 前面省略了缓冲区扩容、缓冲区mark标记处理等代码
    count = pos;
    // 一次性读取多个字节到缓冲区中
    int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
    if (n > 0)
        count = n + pos;
}

BufferedInputStream改进了read()方法,它会先从自身的缓冲区中取字节,当缓冲区的字节取完后,会调用fill()函数把缓冲取一次性填充满,再取字节。


4 总结

(1)不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。

(2)带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多。

(3)有一种情况下,FileInputStream和BufferedInputStream的效率相差不大:即每次读取数据量接近或远超BufferedInputStream的缓冲区大小时(默认8MB),两者效率就没有明显差别了。


转载请注明出处:http://blog.csdn.net/linxdcn/article/details/72875258

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值