递归获取父类字段需通过反射遍历继承链,从当前类调用getDeclaredFields()逐级向上至Object,收集非静态、非合成字段;需处理字段遮蔽、访问控制、泛型与注解,并注意性能缓存及模块化限制。

要递归获取父类字段,核心是利用反射遍历整个继承链,从当前类开始逐级向上扫描 getDeclaredFields(),并收集所有非静态、非合成(non-synthetic)的字段。Java 的反射 API 不会自动包含父类字段,必须手动向上迭代 Class.getSuperclass() 直到 Object.class 或 null。
获取全部声明字段(含父类)
以下方法返回从当前类到 Object 的所有非静态、非合成字段(按声明顺序,子类在前):
public static List<Field> getAllDeclaredFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null && !clazz.equals(Object.class)) {
for (Field f : clazz.getDeclaredFields()) {
if (!f.isSynthetic() && !Modifier.isStatic(f.getModifiers())) {
fields.add(f);
}
}
clazz = clazz.getSuperclass();
}
return fields;
}
注意:getDeclaredFields() 只返回本类**显式声明**的字段,不包括接口字段或 inherited public 字段;该方法不处理接口、内部类或泛型类型擦除信息。
字段去重与访问控制处理
若存在同名字段(如子类覆盖父类字段),Java 允许字段遮蔽(field hiding),此时需决定是否保留所有同名字段,还是仅取最子类版本。常见策略是“首次出现优先”(即子类字段优先,后续父类中同名字段跳过):
- 用
Set<String>记录已添加的字段名,避免重复 - 调用
f.setAccessible(true)前检查!f.isAccessible(),避免重复设置(可能抛安全异常) - 对
private字段,建议捕获SecurityException并记录警告,而非中断流程
支持泛型与注解扫描
若需进一步提取字段上的泛型类型(如 List<String>)或自定义注解(如 @Id),可在收集字段后增强处理:
- 用
f.getGenericType()获取带泛型的类型,再通过ParameterizedType解析实际类型参数 - 用
f.isAnnotationPresent(MyAnnotation.class)或f.getAnnotations()提取元数据 - 注意:泛型信息仅在源码和编译期存在,运行时已擦除,但字段声明的泛型签名仍可通过反射保留
实际使用注意事项
该方案适用于 ORM 映射、序列化框架、DTO 自动填充等场景,但要注意:
- 性能敏感路径应缓存结果(如用
ConcurrentHashMap<Class, List<Field>>),避免重复反射开销 - 不能获取
final字段的运行时值(除非配合 Unsafe,不推荐) - 模块化环境(Java 9+)下,若字段所在包未对当前模块开放(
opens),setAccessible(true)会失败 - Android 上部分高版本限制反射访问私有字段,需适配
hiddenapi白名单策略

















