推荐直接用 files.copy(),它自动处理缓冲、异常和资源释放;需指定 standardcopyoption.replace_existing,确保父目录存在,并用 resolve() 拼接路径。

用 Files.copy() 最快最安全
Java 7+ 推荐直接用 Files.copy(),它底层自动处理缓冲、异常和资源释放,比手写 FileInputStream/FileOutputStream 更可靠。常见错误是忽略 StandardCopyOption.REPLACE_EXISTING,导致目标文件已存在时抛 FileAlreadyExistsException。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 复制单个文件:调用
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING) - 确保
targetPath的父目录已存在,否则会抛IOException(Files.createDirectories()可提前创建) - 不要用
Paths.get("a.txt")直接拼接路径,避免 Windows/Linux 路径分隔符差异;统一用Paths.get("dir").resolve("file.txt") - 若需监控进度,
Files.copy()不支持回调,得退回到手动流 +BufferedInputStream/BufferedOutputStream
大文件复制必须用缓冲流 + try-with-resources
绕过 Files.copy() 手动实现时,不加缓冲或没关流会导致性能暴跌甚至句柄泄漏。典型现象是复制几百 MB 文件耗时翻倍,或跑几次后报 java.io.IOException: Too many open files。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 固定使用
8192或65536字节缓冲区大小,太小(如 1024)频繁系统调用,太大(如 1MB)无明显收益还占内存 - 必须用 try-with-resources 包裹
InputStream和OutputStream,不能只关一个 - 别用
FileReader/FileWriter复制二进制文件(如图片、jar),会破坏字节内容;一律用FileInputStream/FileOutputStream - 示例关键片段:
try (var in = new FileInputStream(src); var out = new FileOutputStream(dst)) { byte[] buf = new byte[8192]; int len; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); } }
复制整个目录要递归 + 判断类型
Files.copy() 默认不支持目录递归复制,直接传目录路径会抛 IOException: Is a directory。必须自己遍历,且容易漏掉符号链接、权限、最后修改时间等元数据。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
Files.walk(sourceDir)遍历,配合Files.isDirectory()和Files.isRegularFile()分支处理 - 创建子目录用
Files.createDirectories(targetSubDir),不是mkdir()(后者不建父级) - 保留文件属性:复制完再调用
Files.setLastModifiedTime(target, Files.getLastModifiedTime(source)),Windows 下还需额外设可执行位 - 遇到符号链接时,默认复制的是链接本身(非目标),如需解引用,加
LinkOption.NOFOLLOW_LINKS控制
跨文件系统移动时慎用 Files.move()
很多人误以为 Files.move() 只是“剪切”,其实它在同文件系统下是原子重命名,跨分区时会退化为“复制 + 删除”。如果复制中途失败,原文件已被删,新文件不完整——这就是静默数据丢失。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 先用
Files.isSameFile(source, target)或检查source.toFile().getCanonicalFile().getParent()和target.toFile().getCanonicalFile().getParent()是否相同,判断是否跨卷 - 跨卷场景强制走复制流程,确认
Files.copy()成功后再调用Files.delete(source) - 删除前务必校验目标文件长度与源一致:
Files.size(source) == Files.size(target),避免因磁盘满导致复制截断 - 不要依赖
Files.move()的返回值判断成败——它成功只代表重命名成功,不保证内容一致
Files.copy() 的默认行为缺乏控制:比如没传 REPLACE_EXISTING 卡死、没预建父目录、跨平台路径拼接出错。这些细节比算法本身更决定工具是否能稳定交付。










