























Java/Kotlin 的 IO 体系分为两套完全不同的顶层逻辑,理解它们是组合流的前提:
FileOutputStream.write("中文".toByteArray()),它只是把当前系统默认编码转出的字节盲写进去,换台电脑可能就乱码了。Charsets.UTF_8 时,你写进去的 "中文",会被它精确地按照 UTF-8 规则翻译成特定的字节序列(28个字节),再交给底层的 Stream。核心原则:全球化的 App,底层最终落到硬盘上的字节必须是 UTF-8。
EF BB BF)。加上它,Excel 读到前三个字节就会明白:“这是 UTF-8”,从而正确显示。\uFEFF(BOM 的 Unicode 转义形式)。50 4B 即 PK 开头)。如果在最外层强行塞入 BOM 字节,直接破坏了 ZIP 结构,导致 Excel 报错“文件已损坏”。<?xml version="1.0" encoding="UTF-8" standalone="yes"?>。这是 IO 设计模式中经典的装饰器模式,像套娃一样,每一层解决一个特定问题。
你的代码:BufferedWriter(OutputStreamWriter(FileOutputStream(file), Charsets.UTF_8))
newLine() 方法,不用你去管 Windows 是 \r\n 还是 Mac 是 \n。你的代码:ZipOutputStream(FileOutputStream(file))
zos.write("字符串")。因为 ZipOutputStream 是字节流,当你要往 XLSX 里写入具体的 XML 内容时,必须在它内部再套一层字符流桥梁:
ZipOutputStream(FileOutputStream(file)).use { zos ->
zos.putNextEntry(ZipEntry("xl/worksheets/sheet1.xml")) // 准备打包某个内部文件
// 此时必须在 zos 上面再套一层 Writer 来写字符串!
OutputStreamWriter(zos, Charsets.UTF_8).use { writer ->
writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") // 声明编码,不加 BOM!
writer.write("<worksheet>...</worksheet>")
}
zos.closeEntry()
}
对比总结:CSV 是“字符流包字节流”的一根通杆;XLSX 是“字节流包字节流(ZIP),内部局部再包字符流”的复杂结构。
四、 Kotlin 的链式 use:极简且绝对正确的资源释放
你的写法:BufferedWriter(...).use { bw -> ... }
这是 Kotlin 中最标准、最优雅的写法。只需要外层包一个 use,底层原理极其精妙:
· use 的本质:它是一个内联高阶函数,无论 Lambda 里面是正常跑完还是抛出异常,退出前都会在 finally 块中自动调用最外层 BufferedWriter 的 close() 方法。
· 为什么只包一层 use 就够了?(关键机制:传递性关闭)
Java/Kotlin IO 规范有严格的契约:外层装饰器关闭时,必须自动调用内层被装饰者的 close()。
当触发 bw.close() 时,源码逻辑是:先自动调用 flush() 把缓冲区数据强制推下去 -> 然后调用 OutputStreamWriter.close() -> 再调用 FileOutputStream.close()。
因此,手动嵌套写 fos.use { osw.use { bw.use { } } } 在底层是完全冗余的。单层链式包装 + 单个 use,既清爽又绝不会发生句柄泄露。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。