能,但仅当源为fileinputstream、目标为fileoutputstream或socket输出流且系统支持底层传输时才高效;否则退化为内存拷贝,且不自动flush/close。

transferTo 方法在 Java 9+ 中能直接替代 while 循环拷贝吗?
能,但只在目标是 OutputStream 且双方都支持高效底层传输时才真正“一键”。它不是万能胶水,更像一个有前提的快捷方式。
常见错误现象:IOException: Stream closed 或实际没写入任何数据——往往因为源流已关闭、目标流不支持缓冲区直传,或调用前已读到末尾。
-
transferTo内部会反复调用read和write,但不保证零拷贝;JDK 9 在FileInputStream→FileOutputStream场景下可能触发transferTo(2)系统调用(Linux)或CopyFile(Windows),否则退化为普通字节数组中转 - 若源流是
ByteArrayInputStream或包装过的BufferedInputStream,它仍走内存拷贝,性能无优势 - 目标流必须支持写入(比如
System.out可以,但只读的InputStream不行)
哪些 InputStream 子类调用 transferTo 效果最好?
效果分三档:系统级零拷贝 > 内核缓冲区搬运 > 用户态内存拷贝。关键看底层是否对接了 OS 的文件传输能力。
- 首选:
FileInputStream→FileOutputStream(同磁盘或支持 sendfile 的文件系统) - 次选:
SocketInputStream(如socket.getInputStream())→FileOutputStream(Linux 下可避免内核态到用户态再回写) - 慎用:
ByteArrayInputStream、ObjectInputStream、任意被FilterInputStream包装过的流——它们的transferTo就是语法糖,底层仍是read(buf)+write(buf)
示例:下面这段看似简洁,实则没省事:
立即学习“Java免费学习笔记(深入)”;
new ByteArrayInputStream(data).transferTo(outputStream); // 等价于手动循环写入 data.length 字节
transferTo 抛出 IOException 的典型原因和排查路径
不是所有 IOException 都意味着代码写错了,有些是资源状态或平台限制导致的。
-
IOException: Stream closed:源流已被 close,或目标流在 transfer 过程中被外部关闭(比如PrintStream底层包装了已关的OutputStream) -
IOException: Not a file(Linux):源是FileInputStream,但文件描述符指向管道、socket 或 proc 文件,无法用transferTo(2) -
IOException: Invalid argument:目标OutputStream不支持write(byte[], int, int)(比如某些自定义日志流只重写了单字节write(int)) - Windows 上对 NFS 挂载点或某些网络驱动器调用失败很常见,此时 JDK 会 fallback 到普通拷贝,但异常仍可能抛出
替代方案对比:transferTo vs. IOUtils.copy vs. 手动 buffer 循环
别迷信“新 API 就一定更好”,得看场景。三方库如 Apache Commons IO 的 IOUtils.copy 其实和 transferTo 底层逻辑高度相似,只是参数更灵活。
- 用
transferTo:适合确定源是FileInputStream、目标是FileOutputStream或 socket 输出流,且不想引入三方依赖 - 用
IOUtils.copy:需要控制 buffer 大小(如IOUtils.copy(in, out, 8192))、支持Reader/Writer、或要统一处理各种流类型 - 手动 buffer 循环:唯一能精确控制每一步(比如边读边解密、限速、打日志),但容易漏掉
available()误判、read返回 -1 后继续写、未处理 partial write 等细节
真正容易被忽略的是:transferTo 不会自动 flush 或 close 任一端流,也不处理字符编码——它只管字节搬运。如果你以为它能替代 Files.copy 做带属性的文件复制,那就错了。









