日志中显示类名@哈希码是因为Object.toString()默认返回getClass().getName()+"@"+Integer.toHexString(hashCode()),而hashCode()通常基于内存地址,导致日志无法体现业务字段;必须重写toString()以输出关键可诊断字段,并注意null安全、性能及双向引用问题。

日志框架打印对象时,默认会调用其 toString() 方法,而这个行为完全依赖于 Object 类的原始实现——即返回 类名@哈希码(如 User@1b6d3586)。这不是格式“规范”,而是 Java 语言层面的默认约定,它不体现业务含义,仅用于区分对象实例。
为什么日志里看到的是类名@哈希码
Object.toString() 的源码是:
return getClass().getName() + "@" + Integer.toHexString(hashCode());
其中 hashCode() 默认基于对象内存地址生成(JVM 实现相关),所以每次运行结果不同。日志框架(Log4j、Logback 等)在遇到未格式化的对象参数(如 log.info("user: {}", user))时,内部就是直接调用 user.toString() 获取字符串。没重写,就只能拿到这个无意义标识。
立即学习“Java免费学习笔记(深入)”;
这个默认输出对日志的实际影响
- 排查问题时无法确认对象关键字段值,比如用户 ID、状态、时间戳是否为空或异常
- 单元测试失败日志中只显示
Expected <User@abc123> but was <User@def456>,看不出具体差异 - 集合类(如 List
)打印时,若元素未重写 toString,整个列表内容不可读 - 某些日志配置开启自动对象展开(如 Log4j2 的
%m{ansi}或 JSON layout),仍会层层调用 toString,但若子对象也未重写,最终还是堆出一堆 @ 符号
日志场景下重写 toString 的实用建议
- 只包含真正有助于诊断的字段,例如 ID、状态、时间(避免打印大文本、二进制、敏感信息如密码)
- 所有引用类型字段用
Objects.toString(field, "null")安全处理 null,防止 NPE 中断日志输出 - 不用字符串拼接(
"id=" + id + ", name=" + name),优先选String.format或 StringBuilder,兼顾可读与性能 - 避免在 toString() 中调用其他复杂对象的 toString(),尤其存在双向关联(如 Order ↔ User)时,会导致栈溢出
- 使用 Lombok 的
@ToString(exclude = {"password", "certData"})要确认排除项符合脱敏要求,且父类字段需显式 include 或加callSuper = true
和日志框架配合的关键注意点
有些框架支持结构化日志(如 Logback 的 LoggingEventCompositeJsonEncoder),这时应让对象走 Jackson 序列化,而不是依赖 toString。因为:
- toString 返回的是扁平字符串,塞进 JSON 字段后变成不可解析的文本
- JSON 序列化能保留类型、嵌套结构、空值语义,更适合监控与 ELK 分析
- 若坚持用 {} 占位符打对象,请确保每个实体类都已重写 toString,否则上线后第一眼日志就失效



















