Object类不提供非空合并功能,因其无字段信息且缺乏反射操作能力;需借助Spring BeanUtils、Apache Commons或手动反射/Getter-Setter实现,并注意基本类型、嵌套对象等边界情况。

Object 类本身不提供对象属性的非空合并与更新功能。这是常见的误解:Java 的 Object 类是所有类的根父类,只定义了如 toString()、equals()、hashCode()、clone() 等基础方法,**不包含任何字段操作、反射赋值或合并逻辑**。所谓“对象属性的非空合并与更新”,必须由开发者借助反射、工具库(如 Apache Commons BeanUtils、Spring BeanUtils)、或手动编码实现。
为什么不能直接用 Object 类做属性合并
Object 是抽象基类,没有字段信息,也不知情子类结构。它无法:
- 获取子类的私有/公有字段列表
- 判断某个字段是否为 null
- 安全地读取或写入字段值(需反射或 getter/setter)
- 区分“未设置”和“显式设为 null”语义
常用可行方案(推荐按场景选择)
1. 使用 Spring 的 BeanUtils.copyProperties()(浅拷贝 + 非空跳过)
Spring 框架提供了扩展支持:通过自定义 BeanWrapper 或配合 BeanUtils.copyProperties(source, target, ignoreProperties),再结合空值判断逻辑实现“仅非空属性覆盖”。
立即学习“Java免费学习笔记(深入)”;
更实用的是继承 BeanUtils 并封装一个安全合并方法:
public static void mergeNonNull(Object source, Object target) {
if (source == null || target == null) return;
Class<?> clazz = target.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object srcVal = field.get(source);
Object tgtVal = field.get(target);
// 仅当源值非 null 且目标值为 null 时更新
if (srcVal != null && tgtVal == null) {
field.set(target, srcVal);
}
} catch (IllegalAccessException e) {
// 忽略不可访问字段(如 final、权限不足)
}
}
}
2. 基于 getter/setter 的通用合并(推荐用于标准 JavaBean)
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
比直接反射字段更安全、符合 JavaBean 规范,且能兼容 Lombok 生成的 accessor:
- 遍历所有
getXXX()方法(排除getClass()、hashCode()等) - 调用对应
setXXX()方法前,先检查 get 返回值是否非 null - 使用
PropertyDescriptor可自动匹配 getter/setter
3. 使用 Apache Commons BeanUtils(注意版本差异)
旧版 BeanUtils.copyProperties() 会覆盖所有属性(含 null);新版(1.9.4+)支持 ConvertUtils.register(...) 自定义转换器,但**仍不原生支持“非空跳过”**。需自行包装:
public static void copyIfNotNull(Object src, Object dest) throws Exception {
PropertyDescriptor[] descriptors = PropertyUtils.getPropertyDescriptors(dest);
for (PropertyDescriptor pd : descriptors) {
String name = pd.getName();
if ("class".equals(name)) continue;
Object value = PropertyUtils.getProperty(src, name);
if (value != null) {
PropertyUtils.setProperty(dest, name, value);
}
}
}
注意事项与边界情况
实际使用中需关注:
-
基本类型字段(如
int、boolean)默认值不是null,无法用“非空”判断是否需要更新,建议统一用其包装类(Integer、Boolean) -
嵌套对象(如
User.address.city)需递归处理,否则只会合并顶层字段 - 集合与 Map 不应简单替换,而要考虑合并逻辑(如 addAll、putAll),这已超出“非空更新”范畴
- final 字段、static 字段、只读属性 无法被反射修改,需提前过滤或抛出提示
更现代的替代思路
若项目允许,可考虑:
- 使用 Record 类 + with() 模式(Java 14+)构建不可变对象,通过 builder 显式控制合并逻辑
- 引入 MapStruct 并自定义
@AfterMapping处理空值策略 - 用 Lombok @Builder.Default + 手动判空构造,避免运行时反射开销
不复杂但容易忽略:真正关键的不是“怎么用 Object”,而是明确你的合并语义——是覆盖、累加、深度合并,还是业务规则驱动?选对工具前,先厘清需求。

















