Java 类之 java.lang.reflect.Field

Java 类之 java.lang.reflect.Field

一、概述

1、java.lang.Class 类获取字段的方法

获取全部公有字段(含继承的,不含私有的)

  1. getField(String name)
    • 获取该类及其父类指定名称的公有字段。
    • 如果字段不存在,则抛出 NoSuchFieldException 异常。
  2. getFields()
    • 获取该类及其父类中所有公有字段的数组。
    • 返回一个 Field 对象数组。

获取本类的所有字段(不含继承的,含私有的)

  1. getDeclaredField(String name)
    • 获取该类中指定名称的任意访问权限的字段,包括私有字段。
    • 如果字段不存在,则抛出 NoSuchFieldException 异常。
  2. getDeclaredFields()
    • 获取该类中所有声明的字段,包括私有字段。
    • 返回一个 Field 对象数组。

代码示例

// 获取公有字段(含继承的,不含私有的)
Field publicField = MyClass.class.getField("publicField");
System.out.println("Public Field: " + publicField);

// 获取所有公有字段(含继承的,不含私有的)
Field[] publicFields = MyClass.class.getFields();
System.out.println("All Public Fields: ");
for (Field field : publicFields) {
    System.out.println(field);
}

// 获取本类的私有字段(不含继承的,含私有的)
Field privateField = MyClass.class.getDeclaredField("privateField");
System.out.println("Private Field: " + privateField);

// 获取本类的所有字段(不含继承的,含私有的)
Field[] allFields = MyClass.class.getDeclaredFields();
System.out.println("All Fields: ");
for (Field field : allFields) {
    System.out.println(field);
}

2、java.lang.reflect.Field 类简介

java.lang.reflect.Field 类是 Java 反射机制中用于表示类的字段(成员变量)的类。它提供了一种在运行时获取和操作类的字段的方式。

3、类定义信息

public final class Field extends AccessibleObject implements Member

二、基本功能

1、基本功能

  1. 获取字段对象:
    • Class.getField(String name): 返回一个 Field 对象,该对象反映了指定公有字段的类或接口。
    • Class.getDeclaredField(String name): 返回一个 Field 对象,该对象反映了指定声明字段的类或接口。
  2. 获取所有字段:
    • Class.getFields(): 返回一个包含某个类或接口的所有公有字段的数组。
    • Class.getDeclaredFields(): 返回一个包含某个类或接口的所有字段的数组,包括私有字段。
  3. 操作字段:
    • Field.get(Object obj): 返回指定对象上此 Field 表示的字段的值。
    • Field.set(Object obj, Object value): 将指定对象上此 Field 表示的字段设置为指定的新值。
  4. 字段信息获取:
    • Field.getName(): 返回字段的名称。
    • Field.getType(): 返回字段的类型(Class 对象)。
    • Field.getModifiers(): 返回表示字段修饰符的整数。
  5. 访问字段修饰符:
    • Modifier.toString(int modifiers): 将修饰符转换为字符串形式。
  6. 设置可访问性:
    • Field.setAccessible(boolean flag): 设置字段的可访问性。如果字段是私有的,需要设置为 true 才能访问。

2、代码示例

import java.lang.reflect.Field;

public class FieldExample {
    public String publicField;
    private int privateField;

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        FieldExample obj = new FieldExample();

        // 获取公有字段对象
        Field publicField = FieldExample.class.getField("publicField");
        System.out.println("Public Field: " + publicField);

        // 获取私有字段对象
        Field privateField = FieldExample.class.getDeclaredField("privateField");
        System.out.println("Private Field: " + privateField);

        // 获取字段的名称、类型和修饰符
        System.out.println("Field Name: " + publicField.getName());
        System.out.println("Field Type: " + publicField.getType());
        System.out.println("Field Modifiers: " + publicField.getModifiers());

        // 获取并设置公有字段值
        publicField.set(obj, "Hello, Public Field!");
        System.out.println("Public Field Value: " + publicField.get(obj));

        // 获取并设置私有字段值(需要设置可访问性)
        privateField.setAccessible(true);
        privateField.set(obj, 42);
        System.out.println("Private Field Value: " + privateField.get(obj));
    }
}

三、扩展

1、关于注解的方法

  1. 获取指定类型的注解:
    • Annotation getAnnotation(Class<? extends Annotation> annotationClass): 返回指定类型的注解对象,如果字段没有指定类型的注解,则返回 null。
  2. 获取所有注解:
    • Annotation[] getAnnotations(): 返回包含此元素上存在的所有注解的数组。
    • Annotation[] getDeclaredAnnotations(): 返回直接存在于此元素上的所有注解的数组,与继承的注解无关。
  3. 判断是否存在指定类型的注解:
    • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass): 如果指定类型的注解存在于此元素上,则返回 true。

2、getAnnotations()getDeclaredAnnotations() 的区别

getAnnotation 方法

  • 该方法用于获取指定类型的注解,如果该注解不存在于该字段上,则返回 null
  • 如果注解存在于该字段上,且是继承关系的,该方法会沿着继承链一直找到最终的注解
  • 这个方法也会考虑到注解的继承,即如果指定类型的注解不存在于当前字段上,但存在于该字段的父类或父接口上,也会被返回。

getDeclaredAnnotation 方法

  • 该方法用于获取指定类型的注解,如果该注解不存在于该字段上,则返回 null
  • getAnnotation 不同的是,getDeclaredAnnotation 不会考虑继承关系,只会在当前字段上查找指定类型的注解

3、其他注意点

访问控制: 如果字段是私有的,确保在使用前调用 setAccessible(true) 方法,以便绕过访问控制检查。否则,在访问私有字段时可能会抛出 IllegalAccessException 异常。

field.setAccessible(true);

静态字段: 如果操作的是静态字段,可以将对象设置为 null,而不需要创建实例。

field.set(null, value); // 设置静态字段
Object value = field.get(null); // 获取静态字段的值

字段类型检查: 在设置字段值之前,最好进行类型检查,以确保所设置的值与字段的类型相匹配。否则,在运行时可能会抛出 IllegalArgumentException

if (field.getType() == int.class) {
    field.setInt(object, 42);
}

异常处理: 在使用反射操作字段时,要考虑可能抛出的异常,如 NoSuchFieldExceptionIllegalAccessException 等。适当地进行异常处理是很重要的。

try {
    Field field = MyClass.class.getDeclaredField("fieldName");
    // 其他操作
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

性能影响: 反射操作相比直接访问字段的性能较差。如果性能是一个关键问题,考虑使用其他手段,如直接访问字段或者使用生成的代码(如使用字节码增强库)。

字段注解: 如果你使用注解,注意使用 getAnnotationgetDeclaredAnnotation 方法时的细微差别,根据需求选择合适的方法。