markSupported

InputStream是否支持mark,默认不支持。

public boolean markSupported() {
return false;
}

InputStream默认是不支持mark的,子类需要支持mark必须重写这三个方法。

在此输入流中标记当前的位置。对 reset 方法的后续调用会在最后标记的位置重新定位此流,以便后续读取重新读取相同的字节。

readlimit 参数告知此输入流在标记位置失效之前允许读取许多字节。

mark

mark接口。该接口在InputStream中默认实现不做任何事情。

public synchronized void mark(int readlimit) {}

reset

reset接口。该接口在InputStream中实现,调用就会抛异常。

public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}

将此流重新定位到对此输入流最后调用 mark 方法时的位置。

reset 的常规协定是:

如果方法 markSupported 返回 true,则:如果创建流以来未调用方法 mark,或最后调用 mark 以来从该流读取的字节数大于最后调用 mark 时的参数,则可能抛出 IOException。如果未抛出这样的 IOException,则将该流重新设置为这种状态:最近调用 mark 以来(或如果未调用 mark,则从文件开始以来)读取的所有字节将重新提供给 read 方法的后续调用方,后接可能是调用 reset 时的下一输入数据的所有字节。

如果方法 markSupported 返回 false,则:对 reset 的调用可能抛出 IOException。如果未抛出IOException,则将该流重新设置为一种固定状态,该状态取决于输入流的特定类型和其创建方式的固定状态。提供给 read 方法的后续调用方的字节取决于特定类型的输入流。

简而言之就是:*调用mark方法会记下当前调用mark方法的时刻,InputStream被读到的位置。

调用reset方法就会回到该位置。*

Code

String content = "yydcdut!";
InputStream inputStream = new ByteArrayInputStream(content.getBytes()); // 判断该输入流是否支持mark操作
if (!inputStream.markSupported()) {
System.out.println("mark/reset not supported!");
}
int ch;
boolean marked = false;
while ((ch = inputStream.read()) != -1) { //读取一个字符输出一个字符
System.out.print((char)ch);
//读到 'c'的时候标记一下
if (((char)ch == 'c')& !marked) {
inputStream.mark(content.length()); //先不要理会mark的参数
marked = true;
} //读到'!'的时候重新回到标记位置开始读
if ((char)ch == '!' && marked) {
inputStream.reset();
marked = false;
}
}
//程序最终输出:yydcdut!dut!

我们知道InputStream是不支持mark的。要想支持mark子类必须重写这三个方法,我想说的是不同的实现子类,mark的参数readlimit作用不尽相同。 常用的FileInputStream不支持mark。

  • 对于BufferedInputStream,readlimit表示:InputStream调用mark方法的时刻起,在读取readlimit个字节之前,标记的该位置是有效的。如果读取的字节数大于readlimit,可能标记的位置会失效。

    在BufferedInputStream的read方法源码中有这么一段:

} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
} else {

为什么是可能会失效呢?

因为BufferedInputStream读取不是一个字节一个字节读取的,是一个字节数组一个字节数组读取的。

例如,readlimit=35,第一次比较的时候buffer.length=0(没开始读)<readlimit.

然后buffer数组一次读取48个字节。这时的read方法只会简单的挨个返回buffer数组中的字节,不会做这次比较。直到读到buffer数组最后一个字节(第48个)后,才重新再次比较。这时如果我们读到buffer中第47个字节就reset。mark仍然是有效的。虽然47>35。

  • 对于InputStream的另外一个实现类:ByteArrayInputStream,我们发现readlimit参数根本就没有用,调用mark方法的时候写多少都无所谓。
public void mark(int readAheadLimit) {
mark = pos;
} public synchronized void reset() {
pos = mark;
}

因为对于ByteArrayInputStream来说,都是通过字节数组创建的,内部本身就保存了整个字节数组,mark只是标记一下数组下标位置,根本不用担心mark会创建太大的buffer字节数组缓存。

  • 其他的InputStream子类没有去总结。原理都是一样的。

所以由于mark和reset方法配合可以记录并回到我们标记的流的位置重新读流,很大一部分就可以解决我们的某些重复读的需要。

这种方式的优点很明显:不用缓存整个InputStream数据。对于ByteArrayInputStream甚至没有任何的内存开销。

当然这种方式也有缺点:就是需要通过干扰InputStream的读取细节,也相对比较复杂。

我是天王盖地虎的分割线

参考:http://zhangbo-peipei-163-com.iteye.com/blog/2022460

InputStream复用,mark和reset的更多相关文章

  1. InputStream中通过mark和reset方法重复利用缓存

    通过缓存InputStream可重复利用一个InputStream,但是要缓存一整个InputStream内存压力可能是比较大的.如果第一次读取InputStream是用来判断文件流类型,文件编码等用 ...

  2. 通过mark和reset方法重复利用InputStream

    InputStreammarkreset 在这篇博客中我们已经简单的知道可以通过缓存InputStream来重复利用一个InputStream,但是这种方式的缺点也是明显的,就是要缓存一整个Input ...

  3. JAVA中mark()和reset()用法

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

  4. InputStream中mark方法使用

    在调用mark的地方做上标记,参数readlimit说明在读取readlimit个字符后书签做废(6.0好像没有失效 ,仍然可以标记位置),使用reset后回到标记的位置.import java.io ...

  5. java.io.IOException: mark/reset not supported

    java.io.IOException: mark/reset not supported at java.io.InputStream.reset(InputStream.java:348) at ...

  6. HTTP协议扫盲(六)InputStream的复用

    一.问题提出 在进行网关引擎开发时,获取到一个http请求的inputstream后,可能要多次利用它进行read操作.由于流读过一次就不能再读了,所以需要实现InputStream的复制. 而Inp ...

  7. 系统学习 Java IO (一)----输入流和输出流 InputStream/OutputStream

    目录:系统学习 Java IO ---- 目录,概览 InputStream 是Java IO API中所有输入流的父类. 表示有序的字节流,换句话说,可以将 InputStream 中的数据作为有序 ...

  8. Java字节流:InputStream OutputStream

    字节输入流:InputStream 类声明: public abstract class InputStream implements Closeable 位于java.io包下,是一个抽象类. 官方 ...

  9. 关于对inputstream流的复制

    今天因为项目需要,获取到一个inputstream后,可能要多次利用它进行read的操作.由于流读过一次就不能再读了,所以得想点办法. 而InputStream对象本身不能复制,因为它没有实现Clon ...

随机推荐

  1. php截取字符串函数

    public function sub_string($str, $len, $charset="utf-8") { if( !is_numeric($len) or $len & ...

  2. Spring CharacterEncodingFilter

    <!-- 配置请求过滤器,编码格式设为UTF-8,避免中文乱码--> <filter> <filter-name>springUtf8Encoding</fi ...

  3. 在JavaScript和C#中获得referer

    1. JavaScript /** * 获取HTTP请求的Referer * @ishost 布尔类型 Referer为空时是否返回Host(网站首页地址) */ function get_http_ ...

  4. 开窗函数 --over()

    一个学习性任务:每个人有不同次数的成绩,统计出每个人的最高成绩. 这个问题应该还是相对简单,其实就用聚合函数就好了. select id,name,max(score) from Student gr ...

  5. Solr术语介绍:SolrCloud,单机Solr,Collection,Shard,Replica,Core之间的关系

    Solr有一堆让人发晕的术语如:collections,shards,replicas,cores,config sets. 在了解这些术语之前需要先做做如下功课: 1)什么是倒排索引? 2)搜索引擎 ...

  6. tomcat常用配置

    一. 增加内存,防止JVM内存溢出 1. 以服务的方式启动时 进入"tomcat安装路径\bin"目录下,打开service.bat文件,找到"--JvmOptions ...

  7. cat,tac,more

    cat VS tac cat是查看文本文件的内容,tac是cat反过来,反向查看文件 $cat 1.txt ls: cannot access ee: No such file or director ...

  8. android 获取本机SMI卡号码

    //获取手机号码 TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); S ...

  9. java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET

    java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了!      社区福利快来领取免费参加MDCC大会机会哦    Tag功能介绍—我们 ...

  10. C++ 之 策略模式

    1  会飞的鸭子 Duck 基类,包含两个成员函数 swim() 和 display():派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写 display() ...