InputStream分析

IO流操作一般分为两类:字符流和字节流。今天我们就来分析下字节输入流的父类:InputStream。它实现了Closeable 接口,我们先来看下Closeable 接口定义,如下:

public interface Closeable extends AutoCloseable {
    // 关闭此流并释放与此流关联的所有系统资源。如果已经关闭该流,则调用此方法无效。
    public void close() throws IOException;
}

属性:

//可跳过最大缓冲去大小
 private static final int MAX_SKIP_BUFFER_SIZE = 2048;

方法: 

 read()

// 从输入流中读取数据的下一个字节,返回值的int其实是byte转化的int
// 子类必须提供此方法的实现
// 如果返回值为-1,说明没有可读的字节了
public abstract int read() throws IOException;

read(byte b[]) 

// 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
 public int read(byte b[]) throws IOException {
        // 调用下边的方法,无偏移的读取b数组长度等同的字节数
        return read(b, 0, b.length);
 }

 read(byte b[], int off, int len)

    // 从输入流中读取最多len个字节,并将其存储在缓冲区数组b中
    // off代表读取的字节从b数组哪个下标开始存入
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            // 如果字节数组b为空,抛出空指针异常
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            // 如果偏移量或者长度不合适,抛出范围异常
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            // 如果读取长度为0,直接返回0
            return 0;
        }
        // 调用read()方法读取一个字节,读取的字节为c
        int c = read();
        if (c == -1) {
            // 如果读取字节为-1,说明没有读取到字节,返回-1即可
            return -1;
        }
        // 将读取到的字节,存入的数组b中偏移量off的下标位置中
        b[off] = (byte)c;
        // 上边已经读取了一个字节,i从1开始,下边要开始循环读取共(len-1)个字节
        int i = 1;
        try {
            for (; i < len ; i++) {
                // 读取下一个字节
                c = read();
                if (c == -1) 
                    // 读取到-1说明读不到字节了,返回-1跳出循环
                    break;
                }
                // 下一个字节存入数组b的(off+i)下标处
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        // 返回读取到的字节个数
        return i;
    }

skip(long n) 

 public long skip(long n) throws IOException {
        // 定义剩余要跳过的字节数量为remaining等于n
        long remaining = n;
        // 定义下边每次循环读取的字节数量为nr
        int nr;
        // 要跳过的字节数n小于等于0,说明不需要跳过字节则直接返回0
        if (n <= 0) {
            return 0;
        }
        // 跳过的字节数量
        // 情况1,要跳过的字节数量大于2048,那么size就取2048
        // 情况2,要跳过的字节数量小于2048,那么size就取要跳过的字节数量
        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        // 根据上一步确定的跳过的字节数size来定义字节数组
        byte[] skipBuffer = new byte[size];
        // 如果剩余要跳过的字节数量大于0就继续循环
        while (remaining > 0) {
           // 从0开始,读取size和remaining间较小数的字节存入skipBuffer中,返回读取字节数量为nr
            // 这个循环中remaining即剩余跳过字节数量是不断减小的,所以有两种情况:
            // 情况1,对应上边的情况1,要跳过的字节数太大,大于2048
            // 要跳过的字节数和剩余的字节数相比,如果剩余的还是太大,那么就读取size个数的字节
            // 情况2,对应上边的情况2
            // 要跳过的字节数大于等于剩余要跳过的字节数,那么就直接读取剩余跳过的字节即可
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            // 如果返回的字节数nr小于0,说明没有读取到字节,跳出循环
            if (nr < 0) {
                break;
            }
            // 剩余的字节数量,等于要减去已经读取了的字节
            // 如果是情况1,多次循环,每次都减去2048,直到剩余的小于2048了,剩下的一次读取即可
            // 如果是情况2,一次循环的读取就OK了,remaining为0
            remaining -= nr;
        }
        // 返回跳过的字节数
        // 如果中途没有读取到输入流末尾,remaining为0,所以返回值为n,即要跳过的都跳过了
        // 如果中途读取到了末尾,那么触发了break,此时remaining仍大于0,
        // 即此时跳过的自然少于需要跳过的大小,则实际跳过的自然要小
        return n - remaining;
    }

available() 

    // 返回可读的字节数
    //  子方法应该重写此方法
    public int available() throws IOException {
        return 0;
    }

close() 

    // 关闭此输入流并释放与流关联的任何系统资源
    public void close() throws IOException {}

mark(int readlimit) 

   // 标记该位置,以便使用reset()方法回滚回该位置
    public synchronized void mark(int readlimit) {}

reset() 

扫描二维码关注公众号,回复: 3999064 查看本文章
   // 回滚回mark()标记的位置
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

 markSupported()

   // 判断是否支持mark()、reset()方法
    public boolean markSupported() {
        return false;
    }

 

 

猜你喜欢

转载自blog.csdn.net/qq_25129863/article/details/83894011