Java IO类库之InputStream

本文详细介绍了Java中的InputStream类,包括其核心方法read、skip等的功能及实现原理,探讨了如何自定义InputStream子类,并解释了mark/reset机制的工作方式。

一、InputStream介绍

/**
 * This abstract class is the superclass of all classes representing
 * an input stream of bytes.
 *
 * <p> Applications that need to define a subclass of <code>InputStream</code>
 * must always provide a method that returns the next byte of input.
 *
 * @author  Arthur van Hoff
 * @see     java.io.BufferedInputStream
 * @see     java.io.ByteArrayInputStream
 * @see     java.io.DataInputStream
 * @see     java.io.FilterInputStream
 * @see     java.io.InputStream#read()
 * @see     java.io.OutputStream
 * @see     java.io.PushbackInputStream
 * @since   JDK1.0
 */

    根据JDK源码注释InputStream是所有字节输入流的超类,并且注释指出如果我们要自己定义一个InputStream子类那么必须重写read方法返回输入流中的下一个字节。好了下面我们来看下这个抽象类定义的几个成员方法。

二、InputStream数据结构

1 - MAX_SKIP_BUFFER_SIZE

// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
// use when skipping.
private static final int MAX_SKIP_BUFFER_SIZE = 2048;

根据注释这个私有的静态成员变量主要用于在skip方法中使用的最大缓存数组大小,也就是skip方法最多能跳过的字节数大小限制。

2 - 成员方法

read方法

    /**
     * 子类必须实现的方法,用于从输入流中读取下一个字节,返回的字节值是基于0到255的int值,
     * 如果到达输入流结束返回-1,否则阻塞直到输入流数据可用或者检测到达流末尾或者异常抛出
     *
     * @return    下一个字节,如果到达流末尾,则返回-1
     * @exception  IOException  如果发生IO异常则抛出
     */
    public abstract int read() throws IOException;

    /**
     * 从输入流中读取一些字节到缓存的字节数组b中.返回实际读取到的字节数,如果数组b的长度为0那么输入流没有
     * 任何字节被读取并返回0,如果输入流在文件末尾即没有字节可读那么返回-1,否则从输入流中依序读取字节到
     * byte数组b中最多读取的字节数为数组b的长度
     * 执行效果与本类方法read(b, 0, b.length)等同实际上就是通过在方法内部调用它实现
     *
     * @param      b  用于保存读取字节的数组
     * @return   读取的字节数,-1表示没有字节可读或者流结束
     * @exception  IOException  输入流结束或则其他一些未知的IO错误导致读取第一个字节失败抛出的异常
     * @exception  NullPointerException  如果缓存字节数组b为空抛出的异常
     */
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    /**
     * 方法逻辑与上述方法类似
     *
     * @param      b     缓存数组
     * @param      off   缓存数组写入输入流读取字节的起始位置
     * @param      len   最多读取的字节长度
     * @return     读取的字节数,-1表示没有字节可读或者流结束
     * @exception  IOException 输入流结束或者为知IO异常导致读取第一个字节失败
     * @exception  NullPointerException 数组b为null
     * @exception  IndexOutOfBoundsException 数组越界异常,off、len为负数,或者len大于缓存数组b长度减去off
     * 
     */
    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) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

skip方法

    /**
     * 跳过输入流n个字节的数据,并返回真正跳过的字节数。如果n为负数表示不跳过任何字节并返回0
     * skip方法会在内部创建一个字节数组,不断读取输入流中的字节到该数组中,直到n个字节被读取或者到达输入流的结尾
     */
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        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 -= nr;
        }

        return n - remaining;
    }

available方法

没什么特别的见名知意,返回输入流中不会被下一次方法调用阻塞的可读取的字节数估计值,因为InputStream是阻塞式的所以available默认返回0

close()方法

    /**
     * 空实现,需子类重写,主要作用是关闭输入流,释放与当前输入流关联的所有系统资源
     */
    public void close() throws IOException {}

markSupport/mark/reset方法

    /**
     * 标记输入流的当前位置,当调用reset方法的时候可以回到上次标记的位置,使得后面可以重新读取相同的字节
     * 参数limit主要告诉输入流允许被读取的字节数目同时这也确定了标记位的最大限制,若超过该限制则标记位失效
     *
     * @param   readlimit  可被读取的最大字节数
     */
    public synchronized void mark(int readlimit) {}

    /**
     * 将流重定位到最后一次对该输入流调用mark方法的位置
     * 当markSupport方法返回true即支持reset/mark方法时,则
     * 1、当mark方法再此之前没有被调用或者自创建流以来从流中读取的字节数已经超过上次调用mark方法时
     * 设置的readLimit那么这两种情况均无法重定位,抛出IOException异常;
     * 2、如果没有抛出IOException异常,那么输入流状态将被重置到上一次mark方法调用时,
     * 上一次mark调用之后读取的字节将可以被后续调用重新读取
     *
     * @exception  IOException  流未被标记或者标记失效,抛出IO异常
     */
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    /**
     * 测试输入流是否支持mark和reset方法,如果支持返回true,不支持返回false,InputStream
     * 默认未实现返回false
     */
    public boolean markSupported() {
        return false;
    }

 

转载于:https://my.oschina.net/zhangyq1991/blog/1853926

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值