天天看点

Java IO - InputStream&OutputStream

基本概念

InpuStream/OuputStream(字节输入流/字节输出流),它们都是抽象类,并且都实现了 Closeable 接口,表示所有字节(byte)输入流(输出流)的类。

源码分析

1.InputStream

类结构如下

Java IO - InputStream&OutputStream

成员变量

// 成员常量,表示 skip(跳跃、丢弃)操作时能丢弃的最大数量
private static final int MAX_SKIP_BUFFER_SIZE = ;
           

构造函数,它有一个默认的无参造函数。

read 方法,定义了三个 read 方法,代表不同的读取方式。

  • ① 是一个抽象方法,留给子类实现。
  • ② 通过调用 ③ 实现。
  • ③ 的实际操作依靠 ① 来完成。
// ①从输入流中读取数据的下一个字节
public abstract int read() throws IOException;

// ②从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
public int read(byte b[]) throws IOException {
    return read(b, , b.length);
}

// ③将输入流中最多 len 个数据字节读入 byte 数组
public int read(byte b[], int off, int len) throws IOException {

    // 校验参数的合法性
    if (b == null) {
        throw new NullPointerException();
    } else if (off <  || len <  || len > b.length - off) {
        throw new IndexOutOfBoundsException();
    } else if (len == ) {
        return ;
    }

    // 先读取一个字节,用来判断是否到流的末尾。若是,返回 -1
    int c = read();
    if (c == -) {
        return -;
    }

    // 将上面读取的一个字节的值添加进数组
    b[off] = (byte) c;

    int i = ;

    // 继续读取流,直到 len 或 流末尾
    try {
        for (; i < len; i++) {
            // 实际通过 read 来进行操作
            c = read();
            if (c == -) {
                break;
            }
            // 字节存放的位置从 off 开始
            b[off + i] = (byte) c;
        }
    } catch (IOException ee) {
    }
    return i;
}
           

skip 方法,它表示跳跃,丢弃操作。

它可以跳过(丢弃)指定数量的字节,然后再继续读取流。

它实际上通过 read 方法来进行跳跃操作,然后创建了一个字节数组用来保存被丢弃的字节。

public long skip(long n) throws IOException {

    long remaining = n;
    int nr;

    // 如果 n 为 0,表示不进行跳跃(丢弃)操作。
    if (n <= ) {
        // 关键 -->返回值为 0 ,说明有 0 个字节被丢弃
        return ;
    }

    // 最多只能丢弃 2048 个字节
    int size = (int) Math.min(MAX_SKIP_BUFFER_SIZE, remaining);

    // 创建数组用来保存被丢弃的字节
    byte[] skipBuffer = new byte[size];

    while (remaining > ) {
        // 关键 --> 通过 read 方法完成该操作
        nr = read(skipBuffer, , (int) Math.min(size, remaining));

        // 如果进行该操作时,提前到达流末尾,则不再继续
        if (nr < ) {
            break;
        }

        // 表示剩余需要丢弃字节的数量
        remaining -= nr;
    }

    return n - remaining;
}
           

剩余方法,大多没有具体的实现,都留给子类做扩展,这里先不细说。

// 返回流中剩下可读取的字节数量,默认返回 0
public int available() throws IOException {
    return ;
}

// 关闭流操作,空方法,留给子类实现
public void close() throws IOException {
}

// 标记操作,空方法,留给子类实现
public synchronized void mark(int readlimit) {
}

// 释放标记,与 mark 配套使用
public synchronized void reset() throws IOException {
    throw new IOException("mark/reset not supported");
}

// 判断是否支持标记,默认不支持
public boolean markSupported() {
    return false;
}
           

2.OutputStream

类结构如下

Java IO - InputStream&amp;OutputStream

构造函数,它有一个无参的构造函数

write 方法,同样有三种写入方式。

  • ① 是一个抽象方法,留给子类实现。
  • ② 通过调用 ③ 实现。
  • ③ 的实际操作依靠 ① 来完成。
// 将指定的字节写入此输出流(抽象方法)
public abstract void write(int b) throws IOException;

// 将 b.length 个字节从指定的 byte 数组写入此输出流
public void write(byte b[]) throws IOException {
    write(b, , b.length);
}

// 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
public void write(byte b[], int off, int len) throws IOException {

    // 校验参数的合法性
    if (b == null) {
        throw new NullPointerException();
    } else if ((off < ) || (off > b.length) || (len < ) || ((off + len) > b.length) || ((off + len) < )) {
        throw new IndexOutOfBoundsException();
    } else if (len == ) {
        return;
    }

    // 将数组中的所有字节内容写入流
    for (int i = ; i < len; i++) {
        write(b[off + i]);
    }
}
           

剩余方法

//将缓存中流的内容强制输出到目的地
public void flush() throws IOException {
}

public void close() throws IOException {
}
           

原理解释

1. 按字节读取

假设下面代码一段字节流

Java IO - InputStream&amp;OutputStream

当字节输入流按字节读取时,即 read():

  • 读取第一个字节,返回值1。
Java IO - InputStream&amp;OutputStream
  • 读取第二个字节,返回值 2.
Java IO - InputStream&amp;OutputStream
  • 读取到最后,发现已经到达流末尾了,返回 -1。
Java IO - InputStream&amp;OutputStream

2.按字节数组读取

假设字节数组的大小为 3,即 read(buffer,0,3):

  • 它会先读取 1 个字节用于判断流是否达到末尾,并将值放入数组的第一个位置(n =0)。
Java IO - InputStream&amp;OutputStream
  • 然后按照循环(n<3)来读取,并将值添加数组
Java IO - InputStream&amp;OutputStream

3.跳跃操作

假设现在跳跃的个数为 3。

Java IO - InputStream&amp;OutputStream

将丢弃的字节添加进缓冲数组。

Java IO - InputStream&amp;OutputStream