Press "Enter" to skip to content

Java反射:getMethods() vs getDeclaredMethods(),别再用错了!

在Java开发中,尤其是编写框架或工具类时,反射(Reflection)是一个强大但又容易混淆的工具。其中,Class 对象的 getMethods()getDeclaredMethods() 方法是获取类方法的两个常用入口,但它们的行为差异巨大,经常成为初学者的困惑点。

今天,我们就来彻底厘清这两者的区别,让你在需要时能够毫不犹豫地做出正确选择。

一、核心区别速览

如果你赶时间,下面这张表和一句话总结可以让你快速了解核心差异:

特性 getMethods() getDeclaredMethods()
查找范围 当前类 + 所有父类 + 实现的接口 仅当前类
访问修饰符 只返回 public 方法 返回所有方法 (public, protected, package-private, private)
继承关系 包含继承来的 public 方法 不包含任何继承来的方法

一句话总结:

  • getMethods(): 获取一个类的 “公共API全家桶”(自己和祖辈的所有 public 方法)。
  • getDeclaredMethods(): 获取一个类的 “私房清单”(仅自己声明的所有方法,无视权限)。

二、代码实战:眼见为实

理论说千遍,不如代码跑一遍。让我们通过一个生动的例子来感受它们的区别。

1. 准备实验材料(类结构)

我们创建一个包含接口、父类和子类的完整结构:

// 接口
interface Flyable {
    void fly(); // 默认是 public abstract
}

// 父类
class Animal {
    public void eat() {
        System.out.println("Animal eats");
    }

    protected void breathe() {
        System.out.println("Animal breathes");
    }

    private void secret() {
        System.out.println("Animal's secret");
    }
}

// 子类 (我们的主角)
class Bird extends Animal implements Flyable {
    public void sing() {
        System.out.println("Bird sings");
    }

    private void nest() {
        System.out.println("Bird builds a nest");
    }

    // 重写接口方法
    @Override
    public void fly() {
        System.out.println("Bird flies");
    }

    // 重写父类方法
    @Override
    public void eat() {
        System.out.println("Bird eats seeds");
    }
}

2. 开始实验

现在,我们用反射来分别检查 Bird 类:

import java.lang.reflect.Method;

public class ReflectionTest {
    public static void main(String[] args) {
        Class<Bird> birdClass = Bird.class;

        System.out.println("----------- 1. 使用 getMethods() on Bird.class -----------");
        // 获取所有公共方法,包括从父类和接口继承的
        Method[] methods = birdClass.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + " (声明于: " + method.getDeclaringClass().getSimpleName() + ")");
        }

        System.out.println("n----------- 2. 使用 getDeclaredMethods() on Bird.class -----------");
        // 只获取在 Bird 类中明确定义的方法,无论访问权限如何
        Method[] declaredMethods = birdClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            // 打印方法名和它的访问修饰符
            System.out.println(method.getName() + " (修饰符: " + java.lang.reflect.Modifier.toString(method.getModifiers()) + ")");
        }
    }
}

3. 结果分析

getMethods() 的输出 (公共API全家桶):

----------- 1. 使用 getMethods() on Bird.class -----------
sing (声明于: Bird)
fly (声明于: Bird)
eat (声明于: Bird)
wait (声明于: Object)
wait (声明于: Object)
wait (声明于: Object)
equals (声明于: Object)
toString (声明于: Object)
hashCode (声明于: Object)
getClass (声明于: Object)
notify (声明于: Object)
notifyAll (声明于: Object)

分析:

  • 自己的 public: sing(), fly(), eat() 都被找到了。
  • 父类的 public: Animal 类没有自己的 public 方法被直接继承(eat被重写),但如果有一个未被重写的 public 方法,也会在这里出现。
  • 接口的 public: fly() 来自 Flyable 接口,被 Bird 实现,所以也包含在内。
  • 祖先类的 public: 所有类都继承自 Object,因此 Object 类的所有 public 方法(如 wait, equals, hashCode 等)也全部被找到。
  • 被排除的: Animalprotected 方法 breathe()private 方法 secret() 没有出现。

getDeclaredMethods() 的输出 (私房清单):

----------- 2. 使用 getDeclaredMethods() on Bird.class -----------
eat (修饰符: public)
sing (修饰符: public)
fly (修饰符: public)
nest (修饰符: private)

分析:

  • 只看自己: 列表里只有Bird.java 文件中明确写下的4个方法。
  • 无视权限: 无论是 publicsing 还是 privatenest,都被找到了。
  • 无视继承: 从 Animal 继承的 breathe() 和从 Object 继承的 wait() 等方法,一个都没有出现。

三、应用场景:我该用哪个?

理解了区别,选择就变得简单了。

场景一:使用 getMethods()

当你需要模拟正常的外部调用,获取一个类的完整公共API时,用它。

  • 动态调用: 比如,根据用户输入或配置文件中的方法名(如setTenantId)来动态调用一个对象的setter。这个setter可能定义在父类中,使用 getMethods() 才能找到。
  • 框架集成: Spring进行依赖注入、Jackson进行JSON序列化时,它们需要发现并调用对象的 public getter/setter 和其他公共方法。

你的例子: // 检查是否存在getTenantId和setTenantId方法(包括继承的方法)
这个需求的目标是找到可用的方法,不管它是在子类还是父类定义的。因此,getMethods() 是完美的选择。

场景二:使用 getDeclaredMethods()

当你需要深入分析一个类的内部结构,无视访问权限时,用它。

  • 单元测试: 使用Mockito等工具时,有时需要操作或验证一个类的私有方法。
  • 代码分析工具: 静态代码分析器、代码覆盖率工具需要检查一个类中定义的所有方法,包括私有的。
  • ORM框架: 像Hibernate这类框架,为了直接操作实体属性,可能会通过反射访问私有字段或其对应的私有getter/setter。

你的例子: // 检查是否存在getTenantId和setTenantId方法
如果你的意图是严格检查当前这个类本身是否定义了这两个方法,而不关心父类,那么 getDeclaredMethods() 是正确的。

四、总结

最后,让我们用一个简单的口诀来结束今天的学习:

  • getMethods = 公开的 (public) + 全家的 (含继承)
  • getDeclaredMethods = 自己的 (declared) + 全部的 (所有修饰符)

记住这个口诀,下次在IDE中敲下 arg.getClass().get... 时,你将胸有成竹!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注