Files.copy() 是 Java 7+ 安全高效拷贝文件的首选,需指定 REPLACE_EXISTING 和 COPY_ATTRIBUTES 参数以避免异常并保留元数据;大文件应退化为带 8KB 缓冲的流式拷贝;目录复制须手动递归;Windows 中文路径需设置 -Dfile.encoding=UTF-8。

用 Files.copy() 最快实现安全拷贝
Java 7+ 推荐直接用 Files.copy(),它底层自动选择最优路径(比如支持 FileChannel.transferTo() 零拷贝),比手动读写流快且不易出错。关键是要传对参数,否则可能覆盖失败或丢元数据。
- 必须显式指定
StandardCopyOption.REPLACE_EXISTING,否则目标文件存在时直接抛FileAlreadyExistsException - 若需保留最后修改时间、权限等属性,额外加
StandardCopyOption.COPY_ATTRIBUTES - 源路径和目标路径都必须是
Path类型,别传String或File——否则编译不通过
Path src = Paths.get("/tmp/data.txt");
Path dst = Paths.get("/backup/data.txt");
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
处理大文件时避免 OutOfMemoryError
用 Files.copy() 拷贝几个 GB 的文件一般没问题,但若在内存受限环境(如嵌入式 JVM 或容器限 256MB)下频繁调用,仍可能触发 GC 压力。此时应退回到带缓冲的流式拷贝,并控制缓冲区大小。
- 缓冲区设为
8192(8KB)是经验值:太小导致系统调用过多,太大无益反而占堆 - 务必用
try-with-resources确保InputStream和OutputStream关闭,漏关会导致句柄泄漏 - 不要用
FileInputStream.read(byte[])不检查返回值——它可能只读部分字节,必须循环直到read()返回-1
try (InputStream in = new FileInputStream(src.toFile());
OutputStream out = new FileOutputStream(dst.toFile())) {
byte[] buf = new byte[8192];
int len;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
}
复制目录必须自己递归,Files.copy() 不支持
Files.copy() 只处理单个文件,传入目录会直接抛 IOException:“Unsupported operation: isDirectory”。要复制整个目录树,得手动遍历 + 判断类型 + 创建子目录。
- 用
Files.walk()获取所有路径,但注意它默认深度优先且不保证顺序——需先建父目录再建子文件 - 用
Files.createDirectories()创建目标路径的父级(它会自动跳过已存在的目录) - 对每个源路径,用
Files.isRegularFile()过滤掉目录,只拷贝文件
Path srcDir = Paths.get("/project/src");
Path dstDir = Paths.get("/backup/src");
Files.walk(srcDir)
.forEach(srcPath -> {
Path dstPath = dstDir.resolve(srcDir.relativize(srcPath));
try {
if (Files.isRegularFile(srcPath)) {
Files.createDirectories(dstPath.getParent());
Files.copy(srcPath, dstPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
Windows 下中文路径乱码?检查默认字符集
在 Windows 上用 Paths.get("C:\用户\文档\file.txt") 报 InvalidPathException 或拷贝后文件名变问号,大概率是 JVM 启动时没指定文件编码。Java 默认用系统 locale 解析路径字符串,而 Windows 中文版默认是 GBK,但 JVM 8+ 在多数情况下按 UTF-8 解析。
立即学习“Java免费学习笔记(深入)”;
- 启动命令加
-Dfile.encoding=UTF-8是最稳妥的解法(尤其当路径来自用户输入或配置文件) - 避免拼接路径字符串,一律用
Paths.get(String...),它内部会做平台适配 - 如果必须用
File构造器,确保字符串本身是合法 Unicode——别用new String(bytes, "GBK")错误解码
真正容易被忽略的是:这个编码问题不会在开发机(UTF-8 环境)暴露,一到客户内网 Windows 机器就崩,而且错误日志里只报“path not found”,得盯住 Paths.get(...).toAbsolutePath() 的输出才能确认是否解析变形。










