美文网首页Java IO
IO流3.InputStream和OutputStream

IO流3.InputStream和OutputStream

作者: ygxing | 来源:发表于2019-06-28 13:18 被阅读0次

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

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

    二.InputStream

    • 所有字节输入流的父类
    • read()操作会一直等待IO堵塞,除非到达流末尾或者抛出异常

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

    /**
     * 抽象输入流
     * 所有字节输入流的父类
     * 定义了输入流的通用方法
     */
    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

    • 所有字节输出流的父类
    • write()操作会一直等待IO堵塞,除非到达流末尾或者抛出异常

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

    /**
     * 抽象输出流
     * 所有字节输出流的父类
     * 定义了通用的字节输出流的通用方法
     */
    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 {
        }
    
    }
    

    相关文章

      网友评论

        本文标题:IO流3.InputStream和OutputStream

        本文链接:https://www.haomeiwen.com/subject/ymsffctx.html