InputStream的mark()、reset()方法

InputStream、FilterInputStream类中并没有实现mark、markSupported和reset这三个方法。所以这两个类无法实现流的复用,即:调用reset方法重复读取已经消费过的流数据。可以通过BufferedInputStream和ByteArrayInputStream两个类来实现流的复用。

1、mark、markSupported和reset含义:

  • java.io.InputStream.markSupported() :该输入流是否支持mark()和reset()方法
  • java.io.InputStream.mark(int readlimit) :标志输入流的当前位置,随后调用reset()方法将该流重新定位到最近标记的位置;参数readlimit表示:在标记位置失效前可以读取字节的最大限制
  • java.ip.InputStream.reset() throws IOException:将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
    • markSupported 返回 true: 如果创建流以后未调用方法 mark,或最后调用 mark 以后从该流读取的字节数大于最后调用 mark 时的参数,则可能抛出 IOException。 如未抛IOException,则将该流重新设置为最近一次调用 mark 以后的位置,从而实现重复消费。
    • markSupported 返回 false:对 reset 的调用可能抛出 IOException,或者reset 不执行任何操作(例如InputStream类)

2、BufferedInputStream类调用mark和reset:

BufferedInputStream重写了父类FilterInputStream的mark和resetf方法,其有支持 mark 和 reset 方法的能力。

BufferedInputStream中,定义了一个byte数组来存放流,文件等数据(俗称缓冲区数组),在构造函数中可以指定缓冲区大小(默认8192)

public class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
    protected volatile byte buf[];
    protected int count;
    protected int pos;
    protected int markpos = -1;
    protected int marklimit;

    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

    public boolean markSupported() {
        return true;
    }
}

根据JAVA官方文档的描述,mark(int readlimit)方法表示,标记当前位置,并保证在mark以后最多可以读取readlimit字节数据,mark标记仍有效。如果在mark后读取超过readlimit字节数据,mark标记就会失效,调用reset()方法会有异常。

但实际的运行情况却和JAVA文档中的描述并不完全相符。 有时候在BufferedInputStream类中调用mark(int readlimit)方法后,即使读取超过readlimit字节的数据,mark标记仍有效,仍然能正确调用reset方法重置。

事实上,mark在JAVA中的实现是和缓冲区相关的。只要缓冲区够大,mark后读取的数据没有超出缓冲区的大小,mark标记就不会失效。如果不够大,mark后又读取了大量的数据,导致缓冲区更新,原来标记的位置自然找不到了。

因此,mark后读取多少字节才失效,并不完全由readlimit参数确定,也和BufferedInputStream类的缓冲区大小有关。 如果BufferedInputStream类的缓冲区大小大于readlimit,在mark以后只有读取超过缓冲区大小的数据,mark标记才会失效。看下面的例子。

private static void test1() {
        InputStream is = null;
        BufferedInputStream buffer = null;
        try {
            is = new FileInputStream("/Users/liuxiao/test");//ABCDE
            buffer = new BufferedInputStream(is);
            //buffer = new BufferedInputStream(is,2);
            System.out.println((char) buffer.read());//A
            System.out.println((char) buffer.read());//B

            buffer.mark(2);// 标记在C的位置,并且设置readlimit为2
            System.out.println("mark() invoked");
            System.out.println((char) buffer.read());//C
            System.out.println((char) buffer.read());//D
            System.out.println((char) buffer.read());//E   此时超出了mark设置的readlimit

            if (buffer.markSupported()) {
                buffer.reset();//这里有可能抛出异常
                System.out.println("reset() invoked");
                System.out.println((char) buffer.read());//C
                System.out.println((char) buffer.read());//D
            } 
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) try {buffer.close();is.close();} catch (IOException e) {e.printStackTrace();}
        }
    }

输出:

如果将BufferedInputStream缓冲区大小设置成2,再次执行就会抛出异常:

简言之BufferedInputStream类调用mark(int readlimit)方法后读取多少字节标记才失效,是取readlimit和BufferedInputStream类的缓冲区大小两者中的最大值,而并非完全由readlimit确定。这个在JAVA文档中是没有提到的。

3、ByteArrayInputStream类调用mark和reset:

ByteArrayInputStream重写了父类InputStream的mark和resetf方法,其有支持 mark 和 reset 方法的能力。

ByteArrayInputStream中,定义了一个byte数组来存放流数据(俗称缓冲区数组),和BufferedInputStream不同的是,ByteArrayInputStream的byte数组包含了整个字节数组,无法在构造函数中指定该缓冲区大小。这也就意味着,ByteArrayInputStream的mark方法参数readlimit根本就没有用,调用mark方法的时候写多少都无所谓(因为byte缓冲区缓冲了整个字节数组,无论mark后读取多少,都不会超过缓冲区大小)

public class ByteArrayInputStream extends InputStream {

    protected byte buf[];
    protected int pos;
    protected int mark = 0;
    protected int count;

    public ByteArrayInputStream(byte buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }

     public boolean markSupported() {
        return true;
    }

    public void mark(int readAheadLimit) {
        mark = pos;
    }

    public synchronized void reset() {
        pos = mark;
    }
}

看一个例子:

private static void test0() {
        InputStream inputStream = new ByteArrayInputStream("ABCDE".getBytes()); 
        
        try {
            System.out.println((char) inputStream.read());//A
            System.out.println((char) inputStream.read());//B
            
            inputStream.mark(2);// 标记在C的位置,设置readlimit=2
            System.out.println("mark() invoked");
            System.out.println((char) inputStream.read());//C
            System.out.println((char) inputStream.read());//D
            System.out.println((char) inputStream.read());//E  超出了mark的readlimit
            
            if (inputStream.markSupported()) {
                inputStream.reset();//不会抛出异常,因为ByteArrayInputStream缓冲了整个字节数组
                System.out.println("reset() invoked");
                System.out.println((char) inputStream.read());//C
                System.out.println((char) inputStream.read());//D
            } 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {inputStream.close();} catch (IOException e) {e.printStackTrace();}
        }
    }

无论makr的参数是多少,都会输出:

参考:

https://www.cnblogs.com/zhang-qiang/articles/2050885.html

https://www.cnblogs.com/chenfei0801/archive/2013/04/01/2994513.html

https://developer.aliyun.com/article/364045

 

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路人儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值