重写 toString() 是日志可读性的底线要求,需用 @Override 标注、返回非 null 字符串、避免 IO/锁/复杂计算,推荐 ToStringBuilder 或 Lombok @ToString 精确控制字段。

Java 中重写 toString() 是最直接有效的日志展示控制方式
默认的 Object.toString() 返回的是 类名@哈希码(如 Person@1b6d3586),对调试和日志毫无信息量。重写它不是“可选优化”,而是日志可读性的底线要求。
实操建议:
- 用
@Override显式标注,避免拼写错误(比如写成toStirng())导致重写失败 - 返回值必须是非
null字符串;若字段可能为null,用Objects.toString(field)安全转换 - 避免在
toString()中触发复杂计算、IO 或锁 —— 日志线程卡住或死锁常源于此 - 字段顺序按业务重要性排列,高频关注字段靠前(比如
id、status优先于createdAt)
用 ToStringBuilder(Apache Commons Lang)减少模板代码
手写字符串拼接易出错(漏逗号、空格不一致、null 异常),尤其字段多时。用 ToStringBuilder 能统一格式、自动处理 null、支持反射快捷生成。
示例:
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("id", id)
.append("name", name)
.append("email", email)
.toString();
}
注意点:
-
SHORT_PREFIX_STYLE输出形如Person[id=123,name=Tom,email=tom@example.com],比默认更紧凑 - 若项目已用 Lombok,
@ToString更轻量,但需确认其exclude/include行为是否符合日志脱敏要求(比如不打印密码字段) - 不要在
toString()里调用其他对象的toString(),除非你 100% 确认对方已安全实现 —— 循环引用会导致栈溢出
日志框架(如 Logback/Log4j2)中开启对象自动序列化需谨慎
有些日志配置允许用 {} 占位符直接传入对象,依赖框架内部调用 toString()。这看似省事,但存在隐性风险:
- 若对象未重写
toString(),日志里只看到Person@xxx,排查时才发现问题,代价远高于提前写好方法 - 某些框架(如旧版 Log4j)对集合、嵌套对象的默认序列化会触发
toString()链式调用,放大性能开销 - JSON 日志场景下,
toString()返回的字符串会被当作文本塞进 JSON 字段,而非结构化数据 —— 此时应改用专门的序列化器(如 Jackson 的@JsonInclude)
Spring Boot + Lombok 场景下的常见陷阱
Lombok 的 @ToString 默认包含所有非静态字段,但容易忽略两点:
- 继承自父类的字段不会被自动包含,需显式加
callSuper = true - 若字段是敏感信息(如
passwordHash),仅靠exclude = "passwordHash"不够 —— 若该字段在父类中,Lombok 默认不处理,仍会泄露 - 启用
@ToString(of = {"id", "name"})可精确控制,但每次加字段都要手动维护,不如重写方法直观 - 测试时别只测单个对象:用
toString()打印含循环引用的对象(如双向关联的User↔Order),看是否抛StackOverflowError
toString() 写得再规范,也挡不住有人在日志里直接写 log.info("user: " + user) —— 这种字符串拼接会强制触发 toString(),但一旦出错就难以定位。最稳妥的做法,是把日志语句写成 log.info("user: {}", user),并确保 user.toString() 已经可靠。

















