JDK 1.7 java.io 源码学习之InputStream和OutputStream

来源:互联网 发布:python 查看硬盘分区表 编辑:程序博客网 时间:2024/04/30 21:04

InputStream和OutputStream是Java IO API 中所有字节输入/输出流的基类,是一个抽象类,实现了Cloaseable接口

InputStream 最核心的是三个read方法:

public abstract int read() throws IOException;
public int read(byte b[]) throws IOException;
public int read(byte b[], int off, int len) throws IOException;

第一个是抽象的read方法是后两个方法的基础,后两个方法均调用了该方法,该方法必须由子类实现。该方法表示一次读取一个字节的数据,如果已经到达流末尾则返回-1

第二个方法等效于第三个方法read(b, 0, b.length)
第三个方法表示一次可以最多读取len个数据字节缓存在byte数组b中,但读取的字节也可能小于该值,以整数的形式返回实际读取的字节数,如果已经到达流末尾则返回-1

public int read(byte b[], int off, int len) throws IOException;

    /*     * b[]是用于缓冲数据的字节数组     * len是最大读取的字节数,但读取的字节也可能小于该值     * off是b[]写入数据时的初始偏移量     */    public int read(byte b[], int off, int len) throws IOException {        if (b == null) {            // 缓存字节数组 未实例化 抛出NullPointException            throw new NullPointerException();        } else if (off < 0 || len < 0 || len > b.length - off) {            /* off偏移量小于0              * 或 最大读取字节数小于0              * 或 最大读取字节数超出了b[]数组长度减去偏移长度             * (即b[]缓冲数组无法保存本次读取的字节数据)             * 抛出IndexOutOfBoundsException             */            throw new IndexOutOfBoundsException();        } else if (len == 0) {            //最大读取字节数是0 直接返回0            return 0;        }        // 读取第一个字节的数据        int c = read();        // 如果到达流末尾了则直接返回-1, 即该文件是个空文件        if (c == -1) {            return -1;        }        //将读取的第一个字节数据存入缓冲数组b[off]中        b[off] = (byte)c;        /*         * 将读取的下一个字节数据存入缓冲数据b[off+1]         * 循环读取下一次字节数据         * 任何情况下,b[0]-b[off] 和 b[off+len]-b[b.length-1] 都不会受影响         */        int i = 1;        try {            for (; i < len ; i++) {                c = read();                // 若读取到流的末尾, 则结束读取                if (c == -1) {                    break;                }                //将读取的字节缓冲的缓冲字节数组内                b[off + i] = (byte)c;            }        } catch (IOException ee) {        }        // 最终读取的字节数        return i;    }

现结合图示演示该方法的执行情况:

FileInputStream fis = new FileInputStream(new File("my.txt"));byte[] b = "123456789".getBytes();int off = 0;int len = 3;fis.read(b, off, len);fis.close();System.out.println(new String(b));

先假设my.txt中的内容如下:

这里写图片描述

这边UTF-8编码集,一个中文占3个字节,数字和英文以及空格占1个字节
my.txt内文件内容的字节数组示意如上图。

byte[] b 是准备用于缓冲读取的字节数据的字节数组,预先放置了一部分数据,方便于比对前后结果,示意如下图:

这里写图片描述

现假设off=0,len=3, 即最多读取3个字节的数据缓存在缓冲字节数组0-2下标处,实际结果示意如下图:

这里写图片描述

缓冲字节数组前三个字节已经被替换为汉字“流”

若off=0,len = 10,则将抛出IndexOutOfBoundsException
因为此时b.length = 9, b.length - off = 9,len > b.length - off ,即缓冲字节数组已经无法缓存一次性读取的字节内容了,放不下

若off=3,len = 3, 则实际结果示意如下图:

这里写图片描述

因为off = 3, 所以其实保存的数组下标需要进行偏移

同理若off=3,len = 8,则将抛出IndexOutOfBoundsException
因为此时b.length = 9, b.length - off = 6,len > b.length - off ,即缓冲字节数组已经无法缓存一次性读取的字节内容了,放不下

skip 方法

    /**     * 跳过多少字节的数据内容     */    public long skip(long n) throws IOException {        // 重新保存该变量        long remaining = n;        int nr;        // 如果跳过的字节数小于等于0, 直接返回0        if (n <= 0) {            return 0;        }        // 跳过的字节数不能超过2048, 取2048 和 传入的跳过字节数的较小值        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);        // 根据需要跳过的字节数创建缓存数组        byte[] skipBuffer = new byte[size];        while (remaining > 0) {            // 读取最大的字节数为跳过的字节数的字节内容            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));            // 如果已经流末尾则跳出循环            if (nr < 0) {                break;            }            //改变remaining变量的值, 减去读取返回的字节数, 用于控制循环              remaining -= nr;        }        //返回实际跳过的字节数        return n - remaining;    }

还是结合上述例子,只是中间执行一次skip方法:

FileInputStream fis = new FileInputStream(new File("my.txt"));byte[] b = "123456789".getBytes();int off = 0;int len = 3;fis.skip(3);fis.read(b, off, len);fis.close();System.out.println(new String(b));

实际结果如下图示意:

这里写图片描述

因为my.txt 的字节内容,被跳过了3个字节的数据,直接从其第4个字节的数据开始读取了

InputStream还有其他几个扩展方法,后续再具体详述

OutputSteam 对应的则有三个核心的write方法:
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException;
public void write(byte b[], int off, int len) throws IOException;

同理第一个是抽象的write方法是后两个方法的基础,后两个方法均调用了该方法,该方法必须由子类实现。该方法表示一次读取一个字节的数据,如果已经到达流末尾则返回-1

第二个方法等效于第三个方法write(b, 0, b.length)
第三个方法表示一次可以最多写出byte字节数组len的字节

public void write(byte b[], int off, int len) throws IOException {        if (b == null) {            throw new NullPointerException();        } else if ((off < 0) || (off > b.length) || (len < 0) ||                   ((off + len) > b.length) || ((off + len) < 0)) {            throw new IndexOutOfBoundsException();        } else if (len == 0) {            return;        }        //逐个字节写入        for (int i = 0 ; i < len ; i++) {            write(b[off + i]);        }    }

首先还是一些异常情况的检查判断:
字节数组未实例化、off小于0、off大于字节数组长度、写入流的字节数小于0、off+len 大于字节数组长度、off+len 小于0 等情况

最后循环调用write(int b),一次一个字节的写入流中

还是结合图示说明:

OutputStream os = new FileOutputStream("my.txt");String content = "流.write 的常规";byte[] b = content.getBytes("UTF-8");os.write(b, 3, 13);os.flush();os.close();

这里写图片描述

实际写入流的是如下内容:

这里写图片描述

OutputStream 内还有close() 和 flush() 方法,这两个方法具体均需要子类去实现

这两个类暂时 看到这里了。

0 0
原创粉丝点击