1. 简介
在 Kotlin 中创建目录,和 Java 一样简单。本文将介绍如何创建单个目录以及嵌套的多级目录结构,帮助你在实际开发中避免踩坑。
虽然 Kotlin 借助 JVM 生态可以直接使用 Java 的 NIO 和 File
类,但不同方法的行为差异仍值得注意,尤其是在异常处理和路径存在性判断方面。
2. 创建单个目录
✅ 推荐方式:Files.createDirectory()
最标准的方式是使用 java.nio.file.Files.createDirectory()
:
Files.createDirectory(Paths.get("./createDirectory"))
这个方法会在目标路径可访问且目录不存在时成功创建目录。✅
⚠️ 但注意:如果父目录不存在,会直接抛出 IOException
:
Files.createDirectory(Paths.get("./createDirectory_2/inner"))
// 抛出 IOException:因为 createDirectory_2 不存在
这种设计其实是“严格模式”——只创建最后一级,不自动补全中间路径。适合需要精确控制的场景。
❌ 替代方式:File.mkdir()
另一个选择是传统 File
类的 mkdir()
方法:
val resultMkdir: Boolean = File("./mkdir").mkdir()
与 Files.createDirectory()
不同,它不会抛异常,而是返回 Boolean
:
true
:创建成功false
:失败(可能是权限不足、父目录不存在等)
⚠️ 踩坑提示:由于没有异常信息,你无法知道失败的具体原因。比如是磁盘满了?还是路径非法?都只能靠猜。因此在关键逻辑中建议优先使用 Files.createDirectory()
。
3. 创建嵌套目录
当需要创建多级目录(如 a/b/c
)时,应使用专门为此设计的方法。
✅ 推荐方式:Files.createDirectories()
这是创建嵌套目录的首选方法:
Files.createDirectories(Paths.get("./createDirectories/inner"))
它的优势在于:
- 自动创建所有中间目录(如
createDirectories
) - 如果目录已存在,不会抛异常,而是静默跳过 ✅
这意味着你可以安全地多次调用:
Files.createDirectories(Paths.get("./createDirectories/inner"))
println("第二次调用不会报错")
Files.createDirectories(Paths.get("./createDirectories/inner")) // 没问题
这在初始化应用数据目录时非常实用,无需先判断是否存在。
❌ 替代方式:File.mkdirs()
对应的传统方法是:
val resultMkdirs: Boolean = File("./mkdirs/inner").mkdirs()
行为类似 createDirectories()
,也会创建中间目录,并返回 Boolean
表示是否成功。
⚠️ 缺点依旧:失败时不抛异常,无法定位具体问题。例如网络挂载点未就绪、权限错误等都会返回 false
,排查成本高。
📌 总结建议:
- 需要精确错误信息 → 使用
Files.createDirectories()
- 只需知道成败 →
mkdirs()
也可接受,但不推荐用于生产环境关键路径
4. 使用 Path 扩展创建父目录链(Kotlin 1.9+)
从 Kotlin 1.9 开始,标准库新增了一个实用扩展函数:createParentDirectories()
,用于确保某个路径的所有父目录都存在。
实际场景:复制文件夹前准备目标路径
假设我们要把 /tmp/source/child
递归复制到 /tmp/dest/child
:
// 准备源目录
File("/tmp/source").mkdirs()
File("/tmp/source/child").mkdirs()
val helloWorldFile = File("/tmp/source/child/helloworld.txt")
helloWorldFile.writeText("Hello, World!")
尝试复制:
assertThrows<NoSuchFileException> {
Path.of("/tmp", "source", "child").copyToRecursively(
Path.of("/tmp", "dest", "child"),
followLinks = false
)
}
❌ 失败!因为 /tmp/dest/child
的父目录 /tmp/dest
根本不存在。
正确做法:先创建父目录链
使用 createParentDirectories()
解决这个问题:
Path.of("/tmp", "dest", "child").createParentDirectories()
Path.of("/tmp", "source", "child").copyToRecursively(
Path.of("/tmp", "dest", "child"),
followLinks = false
)
Assertions.assertTrue(File("/tmp/dest/child").exists())
Assertions.assertTrue(File("/tmp/dest/child/helloworld.txt").exists())
✅ 成功!createParentDirectories()
会确保从根到倒数第二级的所有目录都被创建,非常适合配合 copyToRecursively
或写入新文件前的路径初始化。
💡 提示:这个方法底层其实就是调用了
Files.createDirectories(parent)
,但 API 更直观,语义更清晰。
5. 总结
方法 | 是否抛异常 | 是否创建中间目录 | 推荐场景 |
---|---|---|---|
Files.createDirectory() |
✅ 是 | ❌ 否 | 创建单层目录,需明确失败原因 |
File.mkdir() |
❌ 否 | ❌ 否 | 简单场景,容忍静默失败 |
Files.createDirectories() |
✅ 是 | ✅ 是 | 创建多级目录,推荐生产使用 |
File.mkdirs() |
❌ 否 | ✅ 是 | 兼容旧代码,不推荐新项目 |
Path.createParentDirectories() (Kotlin 1.9+) |
✅ 是 | ✅ 是 | 复制/写入前确保父路径存在 |
📌 最终建议:
- 新项目统一使用
Files.createDirectories()
或createParentDirectories()
,利用异常机制提升健壮性 - 避免使用
mkdir()
/mkdirs()
,除非你真的不在乎失败原因 - Kotlin 1.9+ 用户善用
createParentDirectories()
,让路径操作更安全简洁