CodeGym /课程 /JAVA 25 SELF /获取类、字段、方法的信息

获取类、字段、方法的信息

JAVA 25 SELF
第 62 级 , 课程 1
可用

1. 获取字段信息

getFields()getDeclaredFields() 有何区别

在 Java 中,每个类都有字段(fields)——即在类内部声明的变量。借助反射,我们可以在运行时获知它们的名称、类型、访问修饰符(public/private),甚至访问它们。主要入口是一个 Class<?> 对象。

用于处理字段的 Class 类的主要方法:

方法 返回什么
getFields()
该类、其父类和接口中的所有 public 字段数组
getDeclaredFields()
所有 在该类中声明的字段数组

类比:getFields() 像只参观展厅(public),而 getDeclaredFields() 还会带你走进技术用房(privateprotected、package-private)。

示例:输出类的所有字段

假设我们有如下类:

public class Person {
    public String name;
    private int age;
    protected String email;
}

获取字段信息:

import java.lang.reflect.Field;

Class<?> clazz = Person.class;

// 所有 public 字段(包括继承而来的)
System.out.println("Public fields:");
for (Field field : clazz.getFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

// 所有声明的字段(包括 private,仅本类)
System.out.println("\nDeclared fields:");
for (Field field : clazz.getDeclaredFields()) {
    System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}

结果:

Public fields:
name : String

Declared fields:
name : String
age : int
email : String

如何获取字段的修饰符?

每个字段(Field)都有修饰符(public/private/protectedstaticfinal 等)。可以通过 getModifiers() 获取,并使用 Modifier 转成字符串:

import java.lang.reflect.Modifier;

for (Field field : clazz.getDeclaredFields()) {
    int mods = field.getModifiers();
    System.out.println(field.getName() + " : " + Modifier.toString(mods));
}

2. 获取方法信息

getMethods()getDeclaredMethods()

方法是对象可以执行的操作。借助反射可以获知一个类有哪些方法、它们的参数与返回类型、修饰符以及注解。

方法 返回什么
getMethods()
该类及其父类的所有 public 方法(包括 Object
getDeclaredMethods()
所有 在该类中声明的方法(包括 private

示例:输出类的所有方法

import java.lang.reflect.Method;

System.out.println("Public methods:");
for (Method method : clazz.getMethods()) {
    System.out.println(method.getName());
}

System.out.println("\nDeclared methods:");
for (Method method : clazz.getDeclaredMethods()) {
    System.out.println(method.getName());
}

结果(对于 Person):

Public methods:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait

Declared methods:
(none, if Person has no declared methods)

Person 添加一个方法:

public class Person {
    public String name;
    private int age;
    protected String email;

    public void sayHello() {
        System.out.println("Hi!");
    }
}

现在 getDeclaredMethods() 也会显示它。

如何获取方法的参数和返回类型?

for (Method method : clazz.getDeclaredMethods()) {
    System.out.print(Modifier.toString(method.getModifiers()) + " ");
    System.out.print(method.getReturnType().getSimpleName() + " ");
    System.out.print(method.getName() + "(");
    Class<?>[] params = method.getParameterTypes();
    for (int i = 0; i < params.length; i++) {
        System.out.print(params[i].getSimpleName());
        if (i < params.length - 1) System.out.print(", ");
    }
    System.out.println(");");
}

输出:

public void sayHello();

3. 获取构造器信息

构造器是用于创建对象的特殊方法。

方法 返回什么
getConstructors()
所有 public 构造器
getDeclaredConstructors()
所有 在类中声明的构造器

示例:输出类的所有构造器

import java.lang.reflect.Constructor;

System.out.println("Constructors:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
    System.out.print(clazz.getSimpleName() + "(");
    Class<?>[] params = constructor.getParameterTypes();
    for (int i = 0; i < params.length; i++) {
        System.out.print(params[i].getSimpleName());
        if (i < params.length - 1) System.out.print(", ");
    }
    System.out.println(");");
}

如果类只声明了默认构造器,它也会被输出。

4. 注解:如何知道类、方法或字段上标注了什么

注解是可以添加到类、方法、字段和参数上的特殊标记。通过反射可以获知元素上有哪些注解。

获取注解

  • 对于类:clazz.getAnnotations()
  • 对于方法:method.getAnnotations()
  • 对于字段:field.getAnnotations()

示例:检查是否存在注解 @Deprecated

for (Method method : clazz.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Deprecated.class)) {
        System.out.println(method.getName() + " is @Deprecated");
    }
}

示例:输出类的所有注解

for (var annotation : clazz.getAnnotations()) {
    System.out.println(annotation);
}

5. 实战:用于分析类结构的迷你程序

我们把这些放在一起,编写一个小工具,根据类名输出它的结构:字段、方法、构造器和注解。

代码示例:ClassInspector

import java.lang.reflect.*;
import java.util.Scanner;

public class ClassInspector {
    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入类的全限定名(例如,java.util.ArrayList):");
        String className = scanner.nextLine();

        Class<?> clazz = Class.forName(className);

        System.out.println("\n== 类: " + clazz.getName() + " ==");

        // 类的注解
        System.out.println("注解:");
        for (Annotation annotation : clazz.getAnnotations()) {
            System.out.println("  " + annotation);
        }

        // 字段
        System.out.println("\n字段:");
        for (Field field : clazz.getDeclaredFields()) {
            System.out.println("  " + Modifier.toString(field.getModifiers())
                    + " " + field.getType().getSimpleName()
                    + " " + field.getName());
        }

        // 方法
        System.out.println("\n方法:");
        for (Method method : clazz.getDeclaredMethods()) {
            System.out.print("  " + Modifier.toString(method.getModifiers())
                    + " " + method.getReturnType().getSimpleName()
                    + " " + method.getName() + "(");
            Class<?>[] params = method.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i].getSimpleName());
                if (i < params.length - 1) System.out.print(", ");
            }
            System.out.println(");");
        }

        // 构造器
        System.out.println("\n构造器:");
        for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
            System.out.print("  " + Modifier.toString(constructor.getModifiers())
                    + " " + clazz.getSimpleName() + "(");
            Class<?>[] params = constructor.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i].getSimpleName());
                if (i < params.length - 1) System.out.print(", ");
            }
            System.out.println(");");
        }
    }
}

它如何工作?

  • 用户输入类的全限定名(例如,"java.util.ArrayList" 或你自己的类)。
  • 程序动态加载该类,并输出它的注解、字段、方法和构造器。
  • 试试 JDK 的标准类——你会看到内部隐藏了多少有趣的内容!

6. 一些有用的细节与可视化

可视化示意:通过反射可以了解到类的哪些信息

graph TD
    A[Class<?>] --> B["getFields/getDeclaredFields"]
    A --> C[getMethods/getDeclaredMethods]
    A --> D[getConstructors/getDeclaredConstructors]
    A --> E[getAnnotations]
    B --> F[Field: 类型、名称、修饰符]
    C --> G[Method: 返回类型、参数]
    D --> H[Constructor: 参数]
    E --> I["Annotation[]"]

表格:到哪里查找信息

想了解什么 如何通过反射获取
所有 public 字段
clazz.getFields()
所有已声明的字段
clazz.getDeclaredFields()
所有 public 方法
clazz.getMethods()
所有已声明的方法
clazz.getDeclaredMethods()
所有 public 构造器
clazz.getConstructors()
所有已声明的构造器
clazz.getDeclaredConstructors()
类的注解
clazz.getAnnotations()
方法/字段的注解
method.getAnnotations(), field.getAnnotations()
修饰符
Modifier.toString(field.getModifiers())

7. 使用反射的常见错误

错误 1:混淆 getFields()getDeclaredFields()
如果你要查找 private 字段,请使用 getDeclaredFields(),而不是 getFields()。前者返回类中声明的所有字段,后者只返回 public(并且包含继承而来的字段!)。

错误 2:未处理受检异常。
许多反射方法会抛出异常(例如,ClassNotFoundExceptionSecurityException)。别忘了处理它们,或在方法签名中声明。

错误 3:未考虑访问修饰符。
访问 private 字段和方法前需要先调用 setAccessible(true),否则会抛出 IllegalAccessException。示例:

Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // 打开对 private 字段的访问
int value = (int) field.get(person);

错误 4:期望只看到自己定义的方法/字段。
getMethods()getFields() 返回的不仅是当前类的 public 成员,还包括其所有父类的成员(包括 Object)。如果你只期望看到自己写的内容,这可能会让你惊讶。

错误 5:未正确检查注解是否存在。
请使用 isAnnotationPresent() 检查特定注解,而不是无谓地手动遍历注解数组。

1
任务
JAVA 25 SELF, 第 62 级, 课程 1
已锁定
秘密装置审计 🕵️
秘密装置审计 🕵️
1
任务
JAVA 25 SELF, 第 62 级, 课程 1
已锁定
古代仪式编目 📜
古代仪式编目 📜
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION