复制大型二进制文件应采用分块读写,推荐JDK 7+的Files.copy(),简洁高效且自动优化;若需手动控制,则用FileInputStream/FileOutputStream配合8KB–64KB缓冲区,注意正确处理read()返回值并及时关闭流。

用字节流复制大型二进制文件,核心是避免一次性读入全部内容到内存,而是采用“分块读写”方式,配合合适的缓冲区大小(通常 8KB~64KB),兼顾性能与内存占用。
使用 FileInputStream + FileOutputStream 配合 byte[] 缓冲区
这是最基础也最可控的方式。手动管理缓冲区,适合需要精细控制或兼容老版本 JDK 的场景。
- 创建固定大小的 byte[] buffer(推荐 8192 或 65536 字节)
- 用 read(buffer) 循环读取,返回实际读到的字节数(可能小于缓冲区长度,最后一次可能为 -1)
- 用 write(buffer, 0, len) 写入对应长度,不能直接 write(buffer)
- 记得在 finally 或 try-with-resources 中关闭流
示例片段:
try (FileInputStream fis = new FileInputStream("src.bin");
FileOutputStream fos = new FileOutputStream("dst.bin")) {
byte[] buf = new byte[8192];
int len;
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
}
用 Files.copy()(推荐,JDK 7+)
简洁、高效、自动优化,底层已使用堆外缓冲和零拷贝(如支持)技术,对大文件更友好。
立即学习“Java免费学习笔记(深入)”;
- 直接传入 Path 对象,无需手动开流
- 支持 StandardCopyOption.REPLACE_EXISTING 等选项
- 内部已处理缓冲与异常,代码量少且不易出错
示例:
Files.copy(Paths.get("src.bin"),
Paths.get("dst.bin"),
StandardCopyOption.REPLACE_EXISTING);
注意点:避免常见坑
- 不要用 read() 单字节读取:效率极低,I/O 调用次数爆炸,大文件可能慢几十倍
- 不要忽略 read() 返回值:直接 write(buffer) 会写入整个数组,包含未读数据的脏字节
- 缓冲区不宜过大:超过几 MB 可能导致 GC 压力或分配失败,8KB–64KB 是经验值
- 考虑使用 NIO 的 transferTo/transferFrom(Linux/Unix 下更优):在支持操作系统的文件系统间复制时,可减少内核态与用户态拷贝
进阶:NIO 零拷贝(适用于超大文件 & 同一文件系统)
当源和目标都在本地文件系统(如 ext4、XFS),且使用 FileChannel 时,transferTo 可触发内核级零拷贝(需底层支持)。
- 源 Channel 调用 transferTo(position, count, targetChannel)
- 要求目标 Channel 是 WritableByteChannel(FileChannel 满足)
- 注意:Windows 上 transferTo 对非 SocketChannel 支持有限,建议优先用 Files.copy
不复杂但容易忽略细节,选对方法比手写循环更重要。日常开发首选 Files.copy;特殊性能场景再考虑 NIO 零拷贝。


















