父类新增同名字段不会吞掉子类字段,而是形成隐式字段遮蔽;子类private字段仍存在但与父类字段互不可见,需通过this/super区分访问,否则易引发取值错乱、调试困难和序列化异常。

父类升级新增同名属性,子类原有字段不会被“吞掉”,但会形成**隐式字段遮蔽(field hiding)**——两者共存却互不可见,容易引发取值错乱、调试困惑和序列化异常。关键不是字段消失,而是访问路径断裂。
确认是否真被“吞”:先看字段实际存在性
Java 中字段不支持覆盖(override),只存在遮蔽(hiding)。父类新增 String id,子类原有 private String id 依然在子类字节码中独立存在,只是:
- 通过
this.id访问的是子类自己的字段 - 通过
super.id(且父类字段非 private)才能访问父类新增字段 - 若父类字段是
private,子类根本无法用super访问它
立即止损:三步定位与隔离
避免运行时才发现值不对,应在编译/构建阶段主动识别冲突:
- 启用编译器警告:
-Xlint:fields(JDK 8+),它会提示 “field hides another field” - 在 IDE 中开启“Field hiding inspection”,如 IntelliJ 的 “Field hides a superclass field” 检查项
- 对子类字段加临时注解(如
@Deprecated(forRemoval = true)),配合 CI 扫描强制提醒
长期治理:从设计上切断遮蔽链
靠人工记住 this/super 区分太脆弱,应重构为可维护结构:
- 将原父子类同名字段统一收归为
protected final String baseId(父类定义),子类不再声明新id,改用组合或计算逻辑扩展语义 - 用 getter 封装访问:父类提供
getId(),子类重写该方法返回定制逻辑,字段本身不暴露也不重复声明 - 对必须保留的子类专属字段,采用命名区分策略,如
childSpecificId或localId,并在 JavaDoc 明确标注用途边界
兼容旧数据:序列化层需显式对齐
JSON/XML 反序列化时,Jackson/Gson 默认按字段名映射。若父类新增 id,而子类已有同名字段,可能造成反序列化结果被覆盖或丢失。
- Jackson:用
@JsonIgnore掩盖子类字段,或用@JsonProperty("child_id")重定向别名 - Gson:通过
FieldNamingStrategy或@SerializedName显式控制字段名映射 - 关键原则:序列化字段名 ≠ 运行时字段名,务必分离关注点

















