PrintWriter支持printf/format格式化输出,使用%s、%d、%f等占位符实现变量嵌入与对齐,需手动flush或启用autoFlush,不抛IO异常但需checkError检测错误,适用于日志、报告等轻量场景。
printwriter 本身不支持格式化变量的“自动替换”(比如像 log4j 的 {} 占位符),但它天然支持 java 的 printf 风格格式化输出(format()、printf()、println(string.format(...))),结合字符串模板和类型安全的参数传入,完全可以实现清晰、可维护、带格式的文本输出与日志打印。
用 printf/ format 精确控制变量嵌入与对齐
PrintWriter 继承自 Writer,提供了 printf(String format, Object... args) 和 format(String format, Object... args) 方法,底层调用 java.util.Formatter,支持完整的格式化语法:
-
%s:字符串,自动调用toString() -
%d:十进制整数(int/long) -
%.2f:保留两位小数的浮点数 -
%1$td-%1$tm-%1$ty:复用第 1 个参数(Date或LocalDateTime)提取日、月、年(需配合Calendar或使用DateTimeFormatter预处理) -
%-15s:左对齐、最小宽度 15 的字符串(适合日志对齐列)
示例:
PrintWriter log = new PrintWriter("app.log", "UTF-8");
log.printf("[%s] [%-5s] User[%d] logged in at %s%n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")),
"INFO",
userId,
Instant.now());
log.flush();
封装日志方法:避免重复写格式模板
直接裸写 printf 易出错且难复用。建议封装常用日志级别方法,把格式逻辑收拢:
- 定义统一时间前缀模板(如
"[%s][%s]%n" → "[10:23:45][WARN]") - 为 INFO/WARN/ERROR 提供重载方法,自动注入级别、时间、线程名
- 支持可变参数,兼容任意数量的格式化变量
简易封装示例:
public class SafeLogger {
private final PrintWriter out;
public SafeLogger(PrintWriter out) { this.out = out; }
public void info(String fmt, Object... args) {
String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
out.printf("[%s][INFO ] %s%n", time, String.format(fmt, args));
}
public void error(String fmt, Object... args) {
String time = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
out.printf("[%s][ERROR] %s%n", time, String.format(fmt, args));
}
}
// 使用:
SafeLogger logger = new SafeLogger(new PrintWriter(System.out));
logger.info("User %s accessed %s with status %d", "alice", "/api/data", 200);
输出到文件 + 自动换行 + 异常防护
实战中常需持久化日志。注意三点:
- 构造
PrintWriter(File f, String csn)时显式指定字符集(如"UTF-8"),避免中文乱码 - 启用自动刷新(
new PrintWriter(out, true))或手动flush(),确保内容及时落盘 - 务必在
try-with-resources或finally中close(),否则缓冲区可能丢失最后几条日志
推荐写法:
try (PrintWriter log = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("audit.log", true), StandardCharsets.UTF_8))) {
log.printf("[%s] %s%n",
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
"Payment processed: order#"+orderId+", amount="+amount);
log.flush(); // 显式刷盘更稳妥
} catch (IOException e) {
System.err.println("Failed to write log: " + e.getMessage());
}
替代方案提醒:何时该换工具?
PrintWriter 适合轻量、可控、无依赖场景(如脚本工具、教学示例、嵌入式日志)。但若需以下能力,建议升级:
- 异步写入:PrintWriter 是同步阻塞的,高并发下会拖慢主流程
-
按大小/时间滚动日志:需自己实现
FileOutputStream切换逻辑 -
结构化日志(JSON):PrintWriter 不提供序列化能力,需搭配
ObjectMapper手动转 - 多输出目标(控制台+文件+网络):需自行管理多个 PrintWriter 实例
生产环境推荐 SLF4J + Logback / Log4j2,它们底层仍可能用到 PrintWriter 或 BufferedWriter,但已封装好上述复杂性。


















