泛型类Message的toString高效实现求助:出现StackOverflowException
解决泛型Message类toString()方法的StackOverflowException问题
这个问题我之前也碰到过,StackOverflowException十有八九是循环引用导致的无限递归——你的泛型参数E、B、C对应的类,大概率持有Message类型的引用,当你在toString()里直接拼接data、from、to时,它们的toString()又会调用Message的toString(),一来一回就无限循环了,栈直接炸掉。
下面给你几个高效的解决思路,都是基于工具类或者可控的手动实现:
1. 用Apache Commons Lang的ToStringBuilder(推荐轻量场景)
这个工具类可以帮你自动生成toString(),还能灵活处理循环引用。如果只是想快速搞定,直接用反射生成,同时避免深度递归:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @Override public String toString() { // SHORT_PREFIX_STYLE会输出类似Message[requestNumber=1, data=xxx...]的格式 // 最后两个false分别是不输出父类字段、不输出transient字段,按需调整 return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE, false, false); }
如果担心某些字段还是会引发循环,可以手动指定要输出的字段,同时给可能循环的对象做简化输出:
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) .append("requestNumber", requestNumber) .append("data", safeToString(data)) .append("from", safeToString(from)) .append("to", safeToString(to)) .append("status", status) .append("replyToRequestNumber", replyToRequestNumber) .toString(); } // 自定义安全toString方法,避免循环引用 private String safeToString(Object obj) { if (obj == null) { return "null"; } // 如果对象是Message类型,只打印关键标识,不触发完整toString() if (obj instanceof Message<?, ?, ?>) { Message<?, ?, ?> msg = (Message<?, ?, ?>) obj; return String.format("Message[requestNumber=%d]", msg.requestNumber); } return obj.toString(); }
2. 用Jackson序列化(适合复杂对象结构)
如果你的泛型参数对应的类结构很复杂,Jackson可以自动处理循环引用,输出JSON格式的字符串,调试起来也清晰:
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; // 全局初始化ObjectMapper,避免重复创建 private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() .enable(SerializationFeature.INDENT_OUTPUT) // 格式化输出,方便阅读 .configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false); // 关闭自引用报错 @Override public String toString() { try { return OBJECT_MAPPER.writeValueAsString(this); } catch (Exception e) { // 序列化失败时 fallback 到默认toString() return super.toString(); } }
3. 手动精简实现(无依赖场景)
如果不想引入任何第三方依赖,就手动控制每个字段的输出,对可能引发循环的泛型对象只打印关键信息,比如:
@Override public String toString() { return String.format("Message[requestNumber=%d, data=%s, from=%s, to=%s, status=%s, replyToRequestNumber=%d]", requestNumber, safePrint(data), safePrint(from), safePrint(to), status, replyToRequestNumber); } private String safePrint(Object obj) { if (obj == null) { return "null"; } // 针对可能包含Message的类型做特殊处理 if (obj instanceof Message<?, ?, ?>) { return ((Message<?, ?, ?>) obj).requestNumber + "(Message)"; } // 其他类型直接用toString(),如果还有其他循环风险可以继续加判断 return obj.toString(); }
总结一下:
- 轻量调试用:Apache Commons Lang的ToStringBuilder最省心;
- 复杂对象结构:Jackson序列化自动处理循环,输出格式清晰;
- 无依赖场景:手动实现加safePrint方法完全可控。
内容的提问来源于stack exchange,提问作者gabi939
相关产品推荐
相关产品推荐

