Object类toString()默认返回“ClassName@hashCode”格式字符串,是调试用兜底输出;必须重写以支持日志、测试断言等场景;推荐用Objects.toString()和String.format()安全实现。

toString() 方法默认行为是什么
Object 类的 toString() 方法默认返回形如 ClassName@hashCode 的字符串,其中 hashCode 是对象哈希码的十六进制表示。这不是你想要的可读内容,而是调试用的“兜底输出”。
常见错误现象:打印自定义对象时看到类似 Person@1b6d3586,误以为是 bug 或配置问题。
- 这个结果由
Object.toString()内部调用getClass().getName()和Integer.toHexString(hashCode())拼接而成 - 只要没重写
toString(),所有类都继承这个行为 - IDE 自动生成的
toString()(如 IntelliJ 的Generate → toString())本质就是覆盖它
什么时候必须重写 toString()
当对象需要被日志、调试、单元测试断言或 API 响应(如 Spring Boot 的 @RestController 返回 POJO)直接序列化为人类可读文本时,不重写 toString() 会导致信息缺失或干扰排查。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 日志中打
log.info("user: {}", user)—— 若未重写,日志里只有地址,无法判断字段值 - JUnit 断言失败时显示
expected:,看不出哪字段不同but was: - Lombok 的
@Data会自动帮你生成toString(),但需确认它包含你关心的字段(比如是否漏了password这类敏感字段)
重写 toString() 的安全写法
别手写字符串拼接,容易出空指针、格式混乱或性能问题。优先用工具类或现代语法。
- 用
Objects.toString()处理可能为null的字段:Objects.toString(name, "N/A") - 用
String.format()或文本块(Java 15+)控制格式,避免 + 拼接:return String.format("Person{name=%s, age=%d}", name, age) - 避免在
toString()中调用耗时操作(如数据库查询、远程调用),它可能被日志框架频繁触发 - 不要在
toString()中抛异常——这会让日志、调试器崩溃,且违反约定
public class Person {
private String name;
private Integer age;
@Override
public String toString() {
return String.format("Person{name=%s, age=%s}",
Objects.toString(name, "null"),
Objects.toString(age, "null"));
}
}
IDEA 和 Lombok 对 toString() 的影响
IDE 自动生成和 Lombok 注解都会覆盖默认实现,但它们的行为差异容易被忽略。
- IntelliJ 默认生成的
toString()不处理null,字段为null时直接拼成"null"字符串,没问题;但若字段是数组或集合,可能触发Arrays.toString()或Collection.toString(),嵌套深时易引发无限递归(比如双向关联的父子对象) - Lombok 的
@ToString默认忽略static和transient字段,可用includeFieldNames = true控制键名显示,用exclude = {"password"}排除敏感字段 - 如果用了 Lombok 却又手动写了
toString(),Lombok 会静默失效——编译期不报错,但你的方法不会被调用
重写 toString() 看似简单,真正麻烦的是字段变更时忘记同步更新、嵌套对象循环引用、以及 null 值在不同上下文中的表现不一致。别让它成为日志里第一个看不懂的字符串。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










