InputStream 源码分析

本文主要探讨了InputStream作为所有输入字节流类的基类,提供了基本的读取字节流的方法。通过对源码的解读,揭示了InputStream的重要功能和内部实现。

InputStream是所有输入字节流类的超类。

下面是对InputStream源码对一些基本解读:

public abstract class InputStream implements Closeable {

    private static final int SKIP_BUFFER_SIZE = 2048;

    private static byte[] skipBuffer;

    // 从输入流中读取下个字节的数据,返回的字节是以int类型表示的(0~255)。
    // 如果已经到了流的末尾,返回-1。
    // 这个方法会被阻断直到输入流可用,已经到了末尾,或者抛出异常。
    public abstract int read() throws IOException;


    // 从流中读取b长度的字节到b中。
    // 以整数形式返回实际读取的字节数。
    // 在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
    
    // 从流中读取最多len长度的字节并存储到b中,返回实际读取的字节数。
    // 这个方法会被阻断直到输入流可用,已经到了末尾,或者抛出异常。
    // 如果数组长度为0,那么不会读取字节,返回0;
    // 否则试着去读取至少一个字节,如果到了流的末尾,返回-1。
    // 第一个读取的字节会被保存到b[0]中,下一个会被保存到b[1]中。读取的字节数最大值是b的长度。b中剩余的部分不受影响。
    // 该方法重复调用read()。如果第一次调用报IOException异常,该异常会被抛到该方法。如果接下来对read()的调用报IOException异常,异常会被获取并当成是文件的结尾。前面读取的字节会被保存到b中,返回读取的字节数。
    // 该方法的默认实现会阻断直到已经读取len长度字节的数据,检测到文件末尾,或者有异常抛出。子类可以提供更高效的实现。
    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 {
            for (; i < len; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte) c;
            }
        } catch (IOException ee) {
        }
        return i;
    }


    // 从输入流中跳过并丢弃 n 个字节的数据。
    // 出于各种原因,skip 方法最终跳过的字节数可能更少一些,甚至可能为0。
    // 这可能是一些因素导致的,在跳过n个字节之前已经到达文件末尾只是其中一种可能。返回实际跳过的字节数。如果n为负值,不跳过任何字节。
    // 该方法创建一个字节数组并不断地读取数据放到里面直至读取到n个字节或者到达流末尾。子类可以提供更高效的方法实现。
    public long skip(long n) throws IOException {
        long remaining = n;
        int nr;
        if (skipBuffer == null)
            skipBuffer = new byte[SKIP_BUFFER_SIZE];
        byte[] localSkipBuffer = skipBuffer;
        if (n <= 0) {
            return 0;
        }
        while (remaining > 0) {
            nr = read(localSkipBuffer, 0,
                    (int) Math.min(SKIP_BUFFER_SIZE, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }
        return n - remaining;
    }


    // 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。
    // 某些子类会返回流的所有字节数,有些不会。根据这个返回值来分配一个缓冲区来存取流中的所有数据是不对的。
    // 子类需要重写该方法。
    public int available() throws IOException {
        return 0;
    }

    // 关闭流并且释放所有和这个流关联的系统资源
    public void close() throws IOException {
    }

    // 在流中标记当前位置。以后再调用reset时就可以再回到这个mark过的地方,这样再次读取到同样的字节。
    // readlimit参数告诉系统,在读出这么多个字符之前,这个mark保持有效。
    // mark通常的规范是:如果markSupported返回true,在调用mark方法后,流以某种方式记录所有读取的字节,当调用reset方法后,能够再次提供同样的字节。
    // 但是,在调用reset之前,流不必记录readlimit以外的字节。
    // 在已经关闭的流上调用mark方法对流没有影响
    public synchronized void mark(int readlimit) {
    }


    //  将该输入流重新定位到上一次调用mark方法时标记的位置
    //  如果markSupported返回true:
    //  如果从流创建后,mark还未被调用过,或者调用mark后,读取的字节数大于mark的参数readlimit,可能会抛出IOException异常。
    //  如果异常没有抛出,上次调用mark方法读取的所有字节(从文件的起始位置,如果mark方法没有调用)能够再次提供给以后的read方法,后跟任何从调用 reset 时起将作为下一输入数据的字节。
    //  如果markSupported返回false:
    //  对 reset 的调用可能抛出 IOException
    //  如果未抛出 IOException,则将该流重新设置为一种固定状态,该状态取决于输入流的特定类型及其创建方式。
    //  提供给 read 方法后续调用者的字节取决于特定类型的输入流。
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    // 查看输入流是否支持mark和reset方法。对于某个特定的流实例来说,是否支持mark和reset方法是不变的属性。
    public boolean markSupported() {
        return false;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值