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 上用 Paths.get("C:\\用户\\文档\\file.txt") 报 InvalidPathException 或拷贝后文件名变问号,大概率是 JVM 启动时没指定文件编码。Java 默认用系统 locale 解析路径字符串,而 Windows 中文版默认是 GBK,但 JVM 8+ 在多数情况下按 UTF-8 解析。
-Dfile.encoding=UTF-8 是最稳妥的解法(尤其当路径来自用户输入或配置文件)Paths.get(String...),它内部会做平台适配File 构造器,确保字符串本身是合法 Unicode——别用 new String(bytes, "GBK") 错误解码真正容易被忽略的是:这个编码问题不会在开发机(UTF-8 环境)暴露,一到客户内网 Windows 机器就崩,而且错误日志里只报“path not found”,得盯住 Paths.get(...).toAbsolutePath() 的输出才能确认是否解析变形。