重写 toString 应避免内存开销:不创建临时对象、不装箱、不字符串拼接、不递归调用;优先用预容量 StringBuilder 直接 append 原生类型;防护循环引用、限制集合输出、截断大字段;推荐 Lombok @ToString。

重写 toString 时避免内存开销,核心是**不创建临时对象、不触发装箱、不拼接字符串、不递归调用**。尤其在高频日志或大量对象打印场景下,低效的 toString 会显著增加 GC 压力和堆内存占用。
用 StringBuilder 替代 + 拼接
字符串拼接("a" + field1 + ", " + field2)在 Java 8+ 虽会被编译器优化为 StringBuilder,但仅限于**编译期常量拼接**;若含变量或运行时值,仍可能生成多个中间 String 对象。显式使用 StringBuilder 更可控、更轻量:
@Override
public String toString() {
return new StringBuilder(64) // 预估容量,避免扩容
.append("User{id=")
.append(id) // int 直接 append,无装箱
.append(", name='")
.append(name) // String 引用直接 append
.append("', age=")
.append(age) // int 不调用 String.valueOf
.append('}')
.toString();
}
注意:StringBuilder.append(int) 等原生类型方法内部复用字符数组,不新建 String;而 String.valueOf(int) 或 Integer.toString() 会创建新字符串对象。
避免递归引用与深度遍历
若对象图存在循环引用(如 A → B → A),或字段本身是复杂集合/嵌套对象,直接调用其 toString() 可能引发栈溢出或生成超长字符串,间接导致内存飙升。应做防护:
立即学习“Java免费学习笔记(深入)”;
- 对自身引用字段(如 parent、next)只打印类名 + hashCode,不调用
toString() - 对集合字段,限制元素数量(如
list.subList(0, Math.min(5, list.size())))并标注省略 - 用
System.identityHashCode(this)替代this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()),避免字符串拼接开销
谨慎处理 null 和大字段
null 值直接拼接会变成字符串 "null",看似无害,但若字段是 byte[]、String(MB 级)、或 Map(百万条目),直接 toString() 可能瞬间分配数 MB 内存:
- 对大文本字段(如
content),只取前 N 字符 +"..."(用String.substring(0, Math.min(n, s.length()))) - 对字节数组,不调用
Arrays.toString(bytes)(它会全量转为 int 列表再拼接),改用Hex.encodeHexString(bytes, 0, Math.min(16, bytes.length)) + "..." - 对
null字段统一用"null"字面量,不调用String.valueOf(null)(它内部也返回字面量,但多一层方法调用)
考虑用 Lombok 或专用工具生成
手写易漏边界情况。Lombok 的 @ToString 默认安全:跳过 static 和 transient 字段,支持 exclude/of 显式控制,且生成代码直接调用 StringBuilder;若需极致性能,可用 javapoet 或注解处理器生成定制化 toString,绕过反射和泛型擦除开销。


















