sequenceinputstream已成历史文物,因不支持mark/reset、available不可靠、异常处理困难,jdk9+推荐用inputstream.concat()替代,或jdk8-用apache commons io的compositeinputstream。

它确实能把多个 InputStream 串成一个,但几乎没人该用它——除非你在维护一段 2003 年的遗留代码。
为什么 SequenceInputStream 现在基本是“历史文物”
这个类设计初衷是把几个流按顺序读完前一个再读后一个,听起来合理,但实际和现代 Java 的流处理逻辑严重脱节:
- 不支持
mark()/reset()—— 只要中间任意一个流不支持,整个链就失效 - 无法提前知道总长度(
available()返回值不可靠,常为 0) - 异常处理极难:某个子流抛
IOException后,你得手动记位置、跳过、续接,没内置恢复机制 - JDK 9+ 引入了
InputStream.concat(),语义更清晰、实现更健壮,且返回的是标准InputStream子类
真要用 SequenceInputStream,必须绕开的三个坑
如果你被逼到非用不可(比如对接老协议或测试框架),注意这三点:
- 构造时传入的
Enumeration<inputstream></inputstream>必须「实时可遍历」——不能是已遍历过的Vector或一次性迭代器,否则会静默吞掉后面所有流 - 每个子流关闭时机由它自己控制:
SequenceInputStream不会帮你关,也不保证读完自动关;漏关会导致文件句柄泄漏 - 如果某个子流返回 -1(EOF)但后续还有流,它会自动切换;但如果某流抛出
NullPointerException(比如传了null流),它直接炸,不给你 catch 的机会
示例中常见错误写法:
new SequenceInputStream(new Vector<>(Arrays.asList(in1, in2)).elements())——
Vector.elements() 返回的 Enumeration 在第一次调用 nextElement() 前就可能失效。
立即学习“Java免费学习笔记(深入)”;
InputStream.concat() 是更安全的替代方案(JDK 9+)
它不是语法糖,底层做了状态隔离和异常封装,行为可预测:
- 接受
InputStream...可变参数,不用包装Enumeration - 任一子流异常时,会把异常包装为
IOException抛出,且不会影响其他流的资源清理 - 支持
markSupported()(只要所有子流都支持) - 返回的流是
java.io.InputStream的匿名子类,和所有现有 API 兼容
简单替换示例:
InputStream merged = InputStream.concat(in1, in2, in3); // ✅替代
InputStream merged = new SequenceInputStream(in1, new SequenceInputStream(in2, in3)); // ❌ 手动嵌套易错
如果必须兼容 JDK 8 及以下,用 Apache Commons IO 的 CompositeInputStream
比原生 SequenceInputStream 多了三件事:
- 构造时校验是否传了
null流,提前报错 - 提供
setAutoClose(true),读完自动关所有子流 - 重写了
toString(),调试时能看清当前在第几个流、已读多少字节
依赖需显式引入:org.apache.commons:commons-io:1.3.2+。注意别用太老的 commons-io:1.3.1,那个版本的 CompositeInputStream 有竞态 bug。
真正麻烦的从来不是怎么合并流,而是合并之后怎么让上层代码不感知差异——比如缓冲区大小突变、阻塞行为变化、或者某个子流慢得拖垮整体超时。这些细节,SequenceInputStream 从没打算帮你扛。










