Java反射中getFields和getDeclaredFields有啥区别

Posted

技术标签:

【中文标题】Java反射中getFields和getDeclaredFields有啥区别【英文标题】:What is the difference between getFields and getDeclaredFields in Java reflectionJava反射中getFields和getDeclaredFields有什么区别 【发布时间】:2013-06-02 17:30:16 【问题描述】:

我对使用Java反射时getFields方法和getDeclaredFields方法之间的区别有点困惑。

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

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

【问题讨论】:

getField 可以获得从超类继承的字段,但 getDeclaredField 不能。 getDeclaredField 限制在你调用函数的类。 @user2336315 是正确的,但是 getField 无法访问私有成员 【参考方案1】:

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。

更新

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

【讨论】:

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

如前所述,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; 【参考方案3】:

public Field[] getFields() throws SecurityException

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

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

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

public Field[] getDeclaredFields() throws SecurityException

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

如果我需要所有父类的所有字段怎么办? 需要一些代码,例如来自https://***.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;

【讨论】:

【参考方案4】:

来自Java Reflection tutorials:

【讨论】:

以上是关于Java反射中getFields和getDeclaredFields有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

JAVA反射中的getFields()方法和getDeclaredFields ()方法的区别

Java反射 getFields和getDeclaredFields的区别

Java反射机制-Declared区别

反射中getFields()与getDeclaredFields()区别

反射之getField()与getDeclaredField()的区别

为啥反射type.GetFields 取不到任何值