Java IO

IO流3.InputStream和OutputStream

2019-06-28  本文已影响0人  ygxing

一.应用程序操作IO的流程

  1. 当进程需要进行IO读写的时候,通过一个特殊的指令,进程进入内核空间, 读取IO到内核空间
  2. 进程再将IO拷贝到用户空间

二.InputStream

常用的字节输入流及其继承关系

/**
 * 抽象输入流
 * 所有字节输入流的父类
 * 定义了输入流的通用方法
 */
public abstract class InputStream implements Closeable {

    //最大可跳过的字节数
    //私有常量
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;
    
    /**
     * 抽象方法,每个子类都需要实现该方法,
     * 从输入流中读取一个字节,
     * 所以返回值的范围是[0,255],
     * 除了ASCII码,其余打印字符都会乱码,
     * 该方法效率较为低下
     * @return 返回的是无符号字节数值,
     *         当读取到文件结尾,那么返回-1
     */
    public abstract int read() throws IOException;

    /**
     * 从流中读取数组b长度的字节,
     * 然后放到字节数组中
     * 注意: 
     *      当返回值小于b.length的时候,
     *      说明b数组没有被填满,
     *      其中[0,返回值]才是真正读取的数据,
     *      而[返回值,b.length]是数组中原有的数据
     *
     * @param b 保存读取数据的字节数组
     * @return 实际读取数据的长度
     *         如果返回-1,说明到了流的末尾
     */
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    /**
     * 从流中读取len长度的字节数据,
     * 然后将数据放到数组b中,放入的位置从off开始,
     * 注意:
     *      当返回值小于len的时候,
     *      说明b数组没有被填满,
     *      其中[off,返回值]才是真正读取的数据,
     *      而[返回值,len]是数组中原有的数据
     *
     * @param b 保存读取流中数据的字节数组
     * @param off 字节数组的写入数据索引的偏移
     * @param len 写入字节数组数据的长度
     * @return 实际读取数据的长度
     *         如果返回-1,说明到了流的末尾
     */
    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) {
            //如果到了流的末尾,那么返回-1
            return -1;
        }
        //将读取到的数据,缓存到b数组中
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                //循环读取数据,一直读到len长度
                c = read();
                if (c == -1) {
                    //到了流的尾部,那么结束循环
                    break;
                }
                //将数据保存到b数组中
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        //返回读取的长度
        return i;
    }

    /**
     * 跳过并丢弃n个字节,
     * 之后的读取操作从n字节之后继续进行
     * @return 返回的是实际跳过的字节数
     */
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }
        
        //跳过的最大长度为MAX_SKIP_BUFFER_SIZE
        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            //跳过操作是通过read()方法来实现的
            nr = read(skipBuffer, 0, (int)Math.min(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标记的位置 
     *
     * 默认不支持标记
     * 所以没有做任何操作
     */
    public synchronized void mark(int readlimit) {}

    /**
     * 重置标记
     * 将流重新定位到上一次调用mark方法时的位置 
     * 
     * 默认不支持标记重置
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    /**
     * 测试输入流是否支持mark和reset方法 
     * 默认不支持标记
     */
    public boolean markSupported() {
        return false;
    }

}

三.OutputStream

常用的字节输入流及其继承关系

/**
 * 抽象输出流
 * 所有字节输出流的父类
 * 定义了通用的字节输出流的通用方法
 */
public abstract class OutputStream implements Closeable, Flushable {

    /**
     * 将一个字节,写入到输出流中
     * 抽象方法,所有的子类都必须实现该方法
     * 该方法效率较低
     */
    public abstract void write(int b) throws IOException;

    /**
     * 将b中的数据,全部写入到输出流中
     */
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    /**
     * 从字节数组b的off位置开始,取出len长度的数据
     * 写入到输出流中
     * @param b 取出数据的字节数组
     * @param off 从b中取出数据的起始位置
     * @param len 从b中取出数据的长度
     */
    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()方法实现
            write(b[off + i]);
        }
    }

    /**
     * 刷新数据
     * 如果流中使用了byte数组缓存
     * 调用该方法会将缓存的byte数组更新到流里面
     */
    public void flush() throws IOException {
    }

    /**
     * 关闭流
     */
    public void close() throws IOException {
    }

}
上一篇下一篇

猜你喜欢

热点阅读