PrintStream适合二进制兼容场景(如System.out),依赖平台编码;PrintWriter适合纯文本日志,支持显式编码(如UTF-8)、更可控的换行与异常处理,推荐用于正式日志文件。

PrintStream 和 PrintWriter 都能格式化输出文本日志,但适用场景和行为有关键区别:PrintStream 更适合写入二进制兼容的字节流(如 System.out),默认使用平台编码且不抛出检查异常;PrintWriter 更适合纯文本日志,支持显式指定字符编码,能更好地控制换行与异常处理。
用 PrintStream 写日志(简单直接,适合控制台或基础文件)
PrintStream 的 print、println、printf 方法天然支持格式化(如 %d、%s、%tF),底层以字节方式写入,自动刷新(若构造时启用 autoFlush),但无法指定字符编码,依赖系统默认编码(可能引发中文乱码)。
- 写入文件时建议包装 FileOutputStream,并传入 true 开启自动刷新
- 避免直接用于需要 UTF-8 的日志文件——除非明确确认系统默认编码是 UTF-8
- 示例:new PrintStream(new FileOutputStream("app.log", true), true) —— 追加模式 + 自动刷新
用 PrintWriter 写日志(推荐用于正式日志文件)
PrintWriter 同样提供 printf/println 等格式化方法,但构造时可传入 OutputStream 或 Writer,并支持显式编码(如 UTF-8),更可控;注意它会吞掉 IOException(仅通过 checkError() 检测),需手动判断错误状态。
- 推荐搭配 OutputStreamWriter 指定编码:new PrintWriter(new OutputStreamWriter(new FileOutputStream("app.log", true), "UTF-8"))
- 调用 flush() 确保内容真正写出(autoFlush 对 printf 不生效,只对 println/println 有效)
- 写完后务必调用 close(),否则缓冲区内容可能丢失
格式化日志的常用写法
两者都支持 String.format 风格的占位符,适合拼接时间、级别、消息等字段:
立即学习“Java免费学习笔记(深入)”;
- 带时间戳的 INFO 日志:pw.printf("[%tF %
- 带异常堆栈:先用 e.printStackTrace(pw)(PrintWriter 支持),PrintStream 也支持但不推荐用于文件(可能截断)
- 避免频繁创建对象:复用同一个 PrintStream/PrintWriter 实例,不要每次写日志都 new 一个
注意事项与避坑点
二者都不具备线程安全的日志轮转、异步写入或日志级别过滤能力,仅适合简单场景。生产环境应优先选用 Log4j、SLF4J + Logback 等专业框架。
- PrintStream 的 printf 在遇到编码不支持的字符时会静默替换(如 ),PrintWriter 在指定 UTF-8 时更可靠
- Windows 和 Linux 换行符不同,%n 在两者中都能正确展开为本地换行符
- 如果日志需被其他程序解析(如 ELK),建议统一用 UTF-8 + LF 换行,并避免 printf 中混用 %n 和 \n


















