files.move() 不能跨文件系统移动文件,仅支持同设备原子移动,跨设备需手动copy+delete并处理异常;重命名需检查路径合法性、显式指定replace_existing;移动目录自动递归但禁止子路径目标。

Files.move() 能否跨文件系统移动文件
不能,Files.move() 默认使用 StandardCopyOption.ATOMIC_MOVE 语义,仅支持同一文件系统内的原子移动。跨磁盘(如从 /home 到 /mnt/usb)会直接抛出 IOException,错误信息通常是 java.nio.file.FileSystemException: Invalid cross-device link。
实际开发中别指望它自动 fallback —— 它不会先 copy 再 delete,必须显式处理:
- 先用
Files.isSameFile()或比较FileStore判断是否同设备 - 不同设备时,改用
Files.copy()+Files.delete()组合,并注意异常时的清理(比如 copy 成功但 delete 失败,得保留原文件并报错) - 若需原子性保障,跨设备场景本质无法原子,只能接受“最终一致”
重命名文件时的常见陷阱
重命名本质是同目录下 move,但容易忽略路径合法性检查。比如目标名含非法字符、长度超限、或目标已存在且未指定覆盖策略。
Files.move() 默认不覆盖,遇到已存在目标会直接抛 FileAlreadyExistsException:
- 要覆盖,必须显式传入
StandardCopyOption.REPLACE_EXISTING - 目标路径不能是目录(除非源也是目录且你意图重命名整个目录),否则抛
IOException:“Is a directory” - Windows 下对大小写不敏感,
foo.txt→FOO.TXT可能静默失败或报错,取决于 JVM 版本和底层 FS 实现
move() 的第三个参数到底该传什么
第三个参数是 CopyOption... 可变参数,常用组合就两个:
-
StandardCopyOption.REPLACE_EXISTING:覆盖目标(最常用) -
StandardCopyOption.ATOMIC_MOVE:强制原子移动(仅同设备有效;若跨设备会直接失败,不会退化)
注意:REPLACE_EXISTING 和 ATOMIC_MOVE 可同时传,但后者优先级更高 —— 如果系统不支持原子跨设备,仍会失败,不会因加了 REPLACE_EXISTING 就自动降级。
别传 StandardCopyOption.COPY_ATTRIBUTES:对 move 操作无效,属性复制不是 move 的语义,JVM 会忽略它(无报错,但也不起作用)。
移动目录时的递归行为
Files.move() 移动目录是原子的,且**自动递归**——不需要手动遍历子文件。但要注意几点:
- 目标路径不能是源目录的子路径(如
/a/b→/a/b/c),否则抛FileSystemException:“Directory not empty” 或 “Invalid argument” - 目标目录若已存在且非空,且没加
REPLACE_EXISTING,会失败;加了则整个目标目录被删除再替换(不是合并) - 符号链接本身可被 move,但默认不跟随链接内容;若想移动链接指向的目标,得先
Files.readSymbolicLink()解析再操作
真正容易被忽略的是权限继承问题:移动后目录的 POSIX 权限(如 rwx)通常保留,但 ACL 或 Windows 的 DACL 可能丢失,尤其跨用户或跨组移动时,得额外调用 Files.setPosixFilePermissions() 或 Files.setAttribute() 补齐。










