ChatGPT解决这个技术问题 Extra ChatGPT

Java反射中getFields和getDeclaredFields有什么区别

在使用 Java 反射时,我对 getFields 方法和 getDeclaredFields 方法之间的区别感到有些困惑。

我读到 getDeclaredFields 允许您访问类的所有字段,而 getFields 只返回公共字段。如果是这种情况,为什么不总是使用 getDeclaredFields

有人可以详细说明一下,并解释这两种方法之间的区别,以及何时/为什么要使用其中一种方法?

getField 可以获得从超类继承的字段,但 getDeclaredField 不能。 getDeclaredField 将自身限制为调用函数的类。
@user2336315 是正确的,但是 getField 无法访问私有成员

P
Paul Rooney

getFields()

整个类层次结构中的所有 public 字段。

getDeclaredFields()

所有字段,无论其可访问性如何,但仅适用于当前类,而不是当前类可能继承的任何基类。

为了使层次结构中的所有字段上升,我编写了以下函数:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

提供 exclusiveParent 类是为了防止从 Object 检索字段。如果您确实需要 Object 字段,它可能是 null

澄清一下,Lists.newArrayList 来自 Guava。

更新

仅供参考,上述代码发布在 GitHub 上我的 LibEx 项目 ReflectionUtils 中。


很好的答案,但应该注意,超类中的私有字段不能被当前类的实例用于 Field#get 和类似方法。换句话说,这种方法不允许当前类访问其超类的私有接口,与典型编译不允许的方式相同。
@Vulcan True,除非编写代码以使用反射通过 setAccessible 更改范围并且没有安全管理器
轻微的,应该是“(无论可访问性)”而不是“(无论范围)”。所有字段都具有相同的范围,即 class's body
@JohnB setAccessible 是否允许子类调用超类的私有成员?我以前从未考虑过它,因为我只在使当前类中的私有成员可访问的上下文中使用它。
它不会。因为 private 字段只能通过特定于类的 getDeclaredFields 访问。每个字段(即使具有相同的类型和名称)都是不同的 Field 实例。
I
IvanRF

如前所述,Class.getDeclaredField(String) 仅查看您调用它的 Class 中的字段。

如果要在 Class 层次结构中搜索 Field,可以使用以下简单函数:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

例如,这对于从超类中查找 private 字段很有用。另外,如果你想修改它的值,你可以像这样使用它:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}

如果根本找不到,稍微修改仍然会抛出错误try try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
C
Community

public Field[] getFields() throws SecurityException

返回一个包含 Field 对象的数组,该数组反映了此 Class 对象表示的类或接口的所有可访问公共字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者它表示数组类、原始类型或 void,则此方法返回长度为 0 的数组。

具体来说,如果此 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果此 Class 对象表示一个接口,则此方法返回此接口及其所有超接口的字段。

此方法不反映数组类的隐式长度字段。用户代码应该使用类 Array 的方法来操作数组。

public Field[] getDeclaredFields() throws SecurityException

返回一个 Field 对象数组,反映由此 Class 对象表示的类或接口声明的所有字段。这包括公共、受保护、默认(包)访问和私有字段,但不包括继承的字段。返回的数组中的元素没有排序,也没有任何特定的顺序。如果类或接口未声明任何字段,或者此 Class 对象表示原始类型、数组类或 void,则此方法返回长度为 0 的数组。

如果我需要所有父类的所有字段怎么办?需要一些代码,例如来自 https://stackoverflow.com/a/35103361/755804

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

S
Somnath Musib

Java Reflection tutorials

https://i.stack.imgur.com/Goad9.png