以下是一个设计用于比较两个同类型对象字段变化的工具类,它使用Java反射机制检查每个字段是否有变化(不考虑递归深度比较):
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class FieldChangeComparator {
/**
* 比较两个同类型对象的所有字段是否有变化
* @param obj1 第一个对象
* @param obj2 第二个对象
* @param <T> 对象类型
* @return Map键为字段名,值为是否变化(true=有变化)
* @throws IllegalArgumentException 对象类型不同或均为null时抛出
*/
public static <T> Map<String, Boolean> compareFields(T obj1, T obj2) {
// 验证对象有效性
if (obj1 == null && obj2 == null) {
throw new IllegalArgumentException("Both objects cannot be null");
} else if (obj1 == null || obj2 == null) {
throw new IllegalArgumentException("One object is null while the other is not");
}
Class<?> clazz = obj1.getClass();
if (!clazz.equals(obj2.getClass())) {
throw new IllegalArgumentException("Objects must be of the same type");
}
Map<String, Boolean> result = new HashMap<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true); // 允许访问私有字段
try {
Object value1 = field.get(obj1);
Object value2 = field.get(obj2);
// 使用Objects.equals进行空安全比较
boolean changed = !Objects.equals(value1, value2);
result.put(field.getName(), changed);
} catch (IllegalAccessException e) {
// 处理访问异常(通常不会发生,因为已setAccessible)
System.err.println("Error accessing field: " + field.getName());
result.put(field.getName(), true); // 按有变化处理
}
}
return result;
}
}
使用示例:
public class Person {
private String name;
private int age;
private Address address; // 自定义引用类型
// 构造方法/getters/setters省略
}
// 测试比较
Person p1 = new Person("Alice", 30, new Address("Paris"));
Person p2 = new Person("Alice", 31, new Address("Paris"));
Map<String, Boolean> changes = FieldChangeComparator.compareFields(p1, p2);
// 输出结果示例:
// name -> false (未变化)
// age -> true (变化)
// address -> false (注意:比较的是引用地址,非内容)
设计说明:
此实现满足快速检测字段级变更的需求,且避免了深度递归带来的复杂性。
-
功能特性:
-
比较同类型对象的所有字段(包括私有字段)
-
返回字段名与变化状态的映射
-
空安全比较(使用
Objects.equals()
) -
自动处理基本类型和包装类型
-
-
关键逻辑:
!Objects.equals(value1, value2)
等价于:
-
value1 == value2
(包括都为null的情况) -
或
value1.equals(value2)
(当两者非null时)
-
-
注意事项:
-
引用类型比较:直接比较对象引用(不递归比较内部字段)
-
静态字段:不比较静态字段(
getDeclaredFields()
不包含静态字段) -
异常处理:
-
类型不同或空对象会抛出
IllegalArgumentException
-
字段访问异常视为有变化(实际极少发生)
-
-
-
性能考虑:
-
适合中小型对象(反射操作有一定开销)
-
对性能敏感场景建议缓存Field数组
-