先从复制一个文件开始。Java NIO 不需要额外依赖,并能把不存在、权限不足和目标冲突作为异常报告。

可运行的单文件复制

保存为 CopyFileDemo.java,Java 8 以上可运行:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class CopyFileDemo {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
throw new IllegalArgumentException("用法:java CopyFileDemo 源文件 目标文件");
}
Path source = Paths.get(args[0]).toRealPath();
Path target = Paths.get(args[1]).toAbsolutePath().normalize();
if (!Files.isRegularFile(source)) {
throw new IllegalArgumentException("源路径必须是普通文件");
}
if (Files.exists(target)) {
throw new IllegalArgumentException("目标已存在,请换一个文件名");
}
Files.createDirectories(target.getParent());
Files.copy(source, target);
System.out.println("已复制 " + Files.size(target) + " 字节");
}
}

在 PowerShell 的空目录中运行:

Terminal window
'hello' | Set-Content -Encoding utf8 source.txt
javac -encoding UTF-8 CopyFileDemo.java
java CopyFileDemo source.txt output/copied.txt
Get-FileHash source.txt, output/copied.txt

两个哈希相同,说明文件内容一致;再次执行会拒绝覆盖。需要覆盖时才显式使用 StandardCopyOption.REPLACE_EXISTING,并先确认目标文件。

目录复制为什么不能直接照搬递归

目录复制还要确定符号链接、空目录、目标冲突和异常中断的处理。特别是把源目录复制到它自己的子目录时,递归可能不断复制新生成的内容。

实现目录工具前,先将源和目标转换为真实/规范路径,拒绝相同路径和源目录内的目标;明确是否跟随链接;使用 Files.walkFileTree 处理目录与文件,传播错误。复制不是备份事务,中途中断时目标可能只有部分内容,不能把“创建了目标目录”当成完成。

参考:Files API。