优先选XSSFWorkbook,但大文件需流式写入防OOM;HSSFWorkbook仅限旧格式且行数受限;样式须复用,中文要设跨平台字体;务必用try-with-resources关闭资源。

用 HSSFWorkbook 还是 XSSFWorkbook?看文件后缀和内存承受力
生成 .xls 用 HSSFWorkbook,生成 .xlsx 用 XSSFWorkbook —— 这不是选功能,是选兼容性和内存开销。老系统要打开就用 HSSFWorkbook,但别指望它支持超过 65536 行;新项目无脑选 XSSFWorkbook,不过它吃内存明显,10 万行数据可能占 200MB+ 堆空间。
常见错误现象:java.lang.OutOfMemoryError: Java heap space 几乎都出在用 XSSFWorkbook 写大文件却没切分或没用流式写入。
-
HSSFWorkbook只支持 Excel 97–2003 格式,不识别.xlsx后缀,强行改后缀会打不开 -
XSSFWorkbook默认启用压缩,但构造时传false(如new XSSFWorkbook(false))可禁用,适合调试时快速查看 XML 结构 - 如果只是导出报表且用户环境固定为 Office 2013+,直接上
XSSFWorkbook,别为了“兼容老版本”自缚手脚
CellStyle 复用不当导致内存爆炸
每调用一次 workbook.createCellStyle() 就新建一个样式对象,Excel 文件里会真实写入一份副本。写 10 万行、每行都 new 一个 style,最终文件可能多出几 MB,JVM 里还堆着上万个 CellStyle 实例。
使用场景:表头加粗居中、数值列右对齐、日期列带格式 —— 这些样式最多复用几十次,不该动态创建上百次。
立即学习“Java免费学习笔记(深入)”;
- 把常用样式提前创建好,存在
Map或静态字段里,按需get,别每次create -
CellStyle不是线程安全的,多线程写同一个Workbook时,别共享未加锁的 style 对象 - 用
cloneStyleFrom()复制已有样式再微调,比从头create更轻量,但注意它仍会新建对象,不是引用
写入中文乱码?重点查字体和编码设置
不是文件编码问题,是 Excel 自身对字体的硬依赖。POI 默认用 Windows 的 宋体(SimSun),但 Linux 服务器通常没这个字体,结果文字变成方块或空格。
错误现象:cell.setCellValue("测试") 在 Excel 里显示为 □□ 或空白,但用 LibreOffice 打开正常 —— 典型字体缺失。
- 显式设置字体:
Font font = workbook.createFont(); font.setFontName("Arial Unicode MS");(跨平台较稳)或"Noto Sans CJK SC"(Linux 友好) - 避免用
font.setCharset(Font.DEFAULT_CHARSET),它在不同 JDK 版本行为不一致;改用font.setCharset(Font.UCS_NATIONAL_CHARSET)更可靠 - 如果必须用中文字体且部署环境可控,提前在服务器安装
simsum.ttc并确认 JVM 能读到(GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts()可验证)
关闭流时忘记 close() 导致文件损坏或锁死
写完不 close() Workbook,文件内容可能只写了一半,或者 Windows 下文件被 Java 进程锁住,无法手动删除/重命名。
性能影响:未关闭的 Workbook 会持续占用内存和临时磁盘(XSSF 会生成 .tmp 文件),批量导出时容易触发磁盘满或 GC 频繁。
- 务必用 try-with-resources:
try (XSSFWorkbook wb = new XSSFWorkbook(); FileOutputStream out = new FileOutputStream("a.xlsx")) { ... wb.write(out); } -
HSSFWorkbook和XSSFWorkbook都实现了AutoCloseable,但FileOutputStream也得包进去,否则write()成功但流没刷盘 - 别在
finally里单独close(),容易掩盖原始异常;try-with-resources 是唯一稳妥方式
最容易被忽略的是:XSSF 在 write() 之后仍需 close() 才真正释放资源,很多人以为 write 完就结束了。










