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(),让路径操作更安全简洁

原始标题:Creating a Directory in Kotlin