You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何ToStringBuilder表现不一致?两次System.out.println(person)输出差异原因

问题解答

1. 两次System.out.println(person)输出不同的原因

首先得明确:System.out.println(obj)本质是调用obj.toString(),所以两次输出不同意味着两次调用person.toString()的结果完全不一样。你觉得中间的person.put("a", "b")无关,其实它间接改变了person的内部状态,进而影响了toString()的执行逻辑。

结合你描述的现象——第一次输出Job@28f67ac7(这是Object类默认的toString()格式:类名@哈希码),第二次输出Manager(Job重写后的toString()结果),核心原因大概率是:

  • 第一次调用person.toString()时,ToStringBuilder没有触发Job对象的toString()调用。可能是person内部的Job属性在此时未被ToStringBuilder的逻辑识别到——比如是延迟加载的属性、反射访问权限受限,或是ToStringBuilder的配置只遍历已存在的Map entry;
  • 执行person.put("a", "b")后,person的内部结构(比如Map的entry集合、Bean的属性缓存)发生了变化,第二次调用person.toString()时,ToStringBuilder的逻辑能正确遍历到Job对应的属性/entry,自然就调用了Job的toString()方法,得到了Manager的结果。

举个常见场景:如果Person是基于Map实现的Bean容器(比如Apache Commons的BeanMap),第一次调用toString()时,Job对应的属性还未被映射到Map的entry中,ToStringBuilder只输出了已有的entry;而put("a", "b")触发了BeanMap的全量属性扫描,第二次调用时Job属性被纳入遍历范围,就出现了不同的输出。

2. ToStringBuilder工作表现不一致的常见原因

结合你的问题,ToStringBuilder出现不稳定表现通常和以下几点有关:

  • 依赖对象的动态状态:尤其是反射模式的ReflectionToStringBuilder,输出完全依赖对象当前的字段/属性状态。如果对象内部结构(比如Map的entry、Bean的属性缓存)在两次调用间发生变化,输出结果自然不同;
  • 配置模式不一致:如果混用了手动字段追加(append()方法)和反射模式,或者反射模式的配置(比如是否忽略transient字段、是否包含父类属性)在运行时被修改,会导致输出逻辑混乱;
  • 延迟加载/初始化的影响:如果被处理的对象包含延迟加载的属性(比如你的Job属性),第一次调用时属性尚未初始化,ToStringBuilder只能输出对象的默认哈希码;后续操作触发属性初始化后,第二次调用就能输出正确的toString()结果;
  • 缓存机制的干扰:某些自定义的ToStringBuilder实现会缓存第一次生成的字符串,如果对象状态变化后缓存未失效,会导致输出和实际状态不符;但你的情况是第一次未调用Job的toString(),所以更可能是缓存未包含后续新增的属性。

快速验证建议

你可以在Job类的toString()方法中加个日志,确认两次调用person.toString()时是否触发了Job.toString():

public class Job {
    @Override
    public String toString() {
        System.out.println("Job.toString()被调用了");
        return "Manager";
    }
}

同时检查Person类的toString()实现,确认ToStringBuilder是如何遍历属性/entry的——如果是基于Map的实现,对比两次调用时entrySet()的内容是否一致。

内容的提问来源于stack exchange,提问作者Brian Schack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:06:10