tostring() 默认返回类名@哈希值,无意义;重写时需用stringbuilder、判空、避免npe;json序列化更规范但需处理循环引用、敏感字段和时间类型等。

toString() 方法不重写,打印出来就是一串无意义的地址
Java 默认的 toString() 返回的是 类名@哈希值十六进制,比如 Person@1b6d3586。这不是 bug,是设计如此——它只保证唯一性,不负责可读性。调试时看到这个,等于没打。
重写的关键是:用 StringBuilder 拼接(避免字符串频繁创建),字段值要判空(防止 NullPointerException),别直接调用其他对象的 toString() 而不检查是否为 null。
- 别写成
return "name=" + name + ", age=" + age;——name为null就崩 - 推荐用
Objects.toString(name, "null")或手动判空 - IDE 自动生成的版本(如 IntelliJ 的
Generate → toString())基本可用,但记得检查嵌套对象是否也重写了toString()
用 JSON 序列化代替 toString()?得看场景
如果目标是日志、API 响应或跨系统调试,JSON 确实比手写 toString() 更规范、易解析。但不是所有情况都适合——比如对象含循环引用、敏感字段、或未实现 Serializable,ObjectMapper 会直接抛异常。
常见错误:com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion,本质是 A 引用 B,B 又引用回 A,Jackson 默认不处理。
- 加
@JsonManagedReference/@JsonBackReference断循环 - 用
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)防止空 Bean 报错 - 日志里用 JSON 要控制深度,避免把整个数据库查出来的集合全打出来
Logback/SLF4J 日志中打印对象,别依赖 toString() 自动触发
写 log.debug("user: {}", user) 看似省事,但 SLF4J 是在日志级别开启时才调用 toString()。如果 toString() 里有耗时操作(比如远程查缓存、拼 SQL),哪怕日志被关了,也会执行——因为参数已传入,只是没输出。
更安全的做法是用延迟求值:
- 写成
log.debug("user: {}", () -> user.toJsonString())(需 SLF4J 2.0+) - 或手动加判断:
if (log.isDebugEnabled()) { log.debug("user: {}", user.toJsonString()); } - 别在
toString()里做 IO、锁、或复杂计算——它可能被任意线程、任意时机调用
Gson vs Jackson:选哪个做 toString() 替代?
两者都能转 JSON,但行为差异明显。Jackson 更主流,但默认不忽略 null 字段;Gson 默认忽略,且对泛型类型擦除更友好(比如 List<string></string> 不用额外传 TypeToken)。
容易踩的坑:
- Jackson 的
ObjectMapper是线程安全的,但别每次 new 一个——复用单例 - Gson 实例也是线程安全的,但
GsonBuilder每次 new,别误当单例用 - 如果用了 Lombok 的
@Data,它生成的toString()不包含父类字段,而 Jackson 默认会序列化父类 public 字段
java.time.LocalDateTime 这类类型,不配序列化器,Jackson 直接报错;Gson 默认不支持,得手动注册 ToDateSerializer。没配好,JSON 就打不出来,又退回去啃那串 @ 地址。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











