Java中getXxx()和getDeclaredXxx()到底有什么区别详解

 更新时间:2026年07月18日 09:29:48   作者:超级苦力怕  
这篇文章主要介绍了Java中getXxx()和getDeclaredXxx()到底有什么区别的相关资料,文中配合实例教你如何快速选对API,避免NoSuchField异常陷阱,需要的朋友可以参考下

前言

写 Java 反射代码时,下面两组调用只差一个 Declared,结果范围却完全不同。

最小现象(Java):

Child.class.getMethod("publicMethod");
Child.class.getDeclaredMethod("childPrivateMethod");

对于字段和方法,不带 Declared 的 API 只查 public,并会沿继承体系搜索;带 Declared 的 API 只查当前类型直接声明的成员,但不限制访问级别。构造器不会继承,接口方法也有静态方法不继承等额外边界。下面按字段、方法、构造器逐层拆开。

一、先给结论:看 public、声明位置和继承范围

反射查找字段、方法和构造器时,getXxx()getDeclaredXxx() 的区别可以归纳为两个问题:

  1. 要不要限制为 public
  2. 要不要沿继承关系查找?
API 类型访问级别是否包含继承成员
getXxx() / getXxxs()只获取 public字段和方法会搜索继承成员
getDeclaredXxx() / getDeclaredXxxs()获取当前类声明的全部访问级别不搜索继承成员

核心规则(文本):

不带 Declared:只查 public;字段和方法会沿继承体系搜索。
带 Declared:只查当前类直接声明的成员,但不限制访问级别。
构造器不会继承,因此构造器 API 始终只查当前类。

这里的“全部访问级别”包括:

  • public
  • protected
  • 包级访问(没有显式修饰符)
  • private

获取与访问是两回事
getDeclaredXxx() 能取得非 public 成员的反射对象,不代表调用方一定能直接读取、修改或调用它。访问控制属于下一步操作,不能与“能否找到成员”混为一谈。

二、用一个继承示例观察边界

继承示例(Java):

class Parent {
    public String publicField;
    protected String protectedField;
    String packageField;
    private String privateField;

    public void publicMethod() {
    }

    protected void protectedMethod() {
    }

    void packageMethod() {
    }

    private void privateMethod() {
    }
}

class Child extends Parent {
    public String childPublicField;
    private String childPrivateField;

    public Child() {
    }

    private Child(String name) {
    }

    public void childPublicMethod() {
    }

    private void childPrivateMethod() {
    }
}

Child.class 执行反射查找时,可以先建立下面这张结果表:

成员getXxx() 能否找到getDeclaredXxx() 能否找到
子类自己声明的 public 成员
子类自己声明的非 public 成员不能
从父类继承的 public 字段或方法不能
父类声明的 protected、包级或 private 成员不能不能

最后一行尤其容易误解。

Declared 的范围(文本):

getDeclaredXxx() 的“全部”只指当前 Class 对象所表示的类直接声明的全部,
不是整条继承链上所有类的全部成员。

如果需要获取父类声明的非 public 成员,必须先拿到父类的 Class 对象,再调用父类的 getDeclaredXxx()

逐层查找父类成员(Java):

Field field = Child.class
        .getSuperclass()
        .getDeclaredField("protectedField");

框架需要扫描整条继承链时,通常会循环调用 getSuperclass(),并对每一层分别执行 getDeclaredFields()getDeclaredMethods()

三、字段:getField 与 getDeclaredField

3.1 查找单个字段

字段查找示例(Java):

Field inheritedPublic =
        Child.class.getField("publicField");

Field childPrivate =
        Child.class.getDeclaredField("childPrivateField");

两组 API 的范围如下:

API查找范围
getField(name)当前类及继承体系中可见的 public 字段
getDeclaredField(name)当前类直接声明的指定字段,不限制访问级别
getFields()当前类及继承体系中可见的全部 public 字段
getDeclaredFields()当前类直接声明的全部字段,不限制访问级别

字段查找失败示例(Java):

Child.class.getField("childPrivateField");
// 非 public,不在 getField() 的范围内

Child.class.getDeclaredField("privateField");
// privateField 由 Parent 声明,不是 Child 直接声明

查找单个字段失败时,会抛出 NoSuchFieldException

3.2 字段不会因为继承就改变声明位置

publicField 可以通过 Child.class.getField("publicField") 找到,但它仍然由 Parent 声明。

确认字段声明位置(Java):

Field field = Child.class.getField("publicField");

System.out.println(field.getDeclaringClass());
// class Parent

getDeclaringClass() 可以用来确认一个成员最初声明在哪个类中。

四、方法:getMethod 与 getDeclaredMethod

4.1 查找单个方法必须给出参数类型

方法可能发生重载,所以按名称查找时还要传入形参类型。

按名称和参数类型查找方法(Java):

Method noArgs =
        Child.class.getMethod("childPublicMethod");

Method method =
        SomeService.class.getDeclaredMethod(
                "save",
                String.class,
                int.class
        );

两组 API 的范围如下:

API查找范围
getMethod(name, parameterTypes...)当前类及继承体系中的 public 方法
getDeclaredMethod(name, parameterTypes...)当前类直接声明的指定方法,不限制访问级别
getMethods()当前类及继承体系中的全部 public 方法
getDeclaredMethods()当前类直接声明的全部方法,不限制访问级别

方法查找示例(Java):

Method inherited =
        Child.class.getMethod("publicMethod");

Method ownPrivate =
        Child.class.getDeclaredMethod("childPrivateMethod");

方法查找失败示例(Java):

Child.class.getMethod("childPrivateMethod");
// private 方法不是 public

Child.class.getDeclaredMethod("protectedMethod");
// protectedMethod 由 Parent 直接声明

查找单个方法失败时,会抛出 NoSuchMethodException

4.2 getMethods() 为什么会出现 Object 的方法

枚举 public 方法(Java):

Method[] methods = Child.class.getMethods();

结果中通常还能看到:

  • toString()
  • equals(Object)
  • hashCode()
  • getClass()
  • wait()
  • notify()
  • notifyAll()

原因不是 Child 重新声明了这些方法,而是 getMethods() 会搜索继承体系中的 public 方法,Object 正位于类继承链的顶端。

调用 method.getDeclaringClass() 可以区分方法究竟声明在 ChildParent,还是 Object

从设计意图看,getMethods() 给出的不是“当前类源码里写了哪些方法”,而是这个类型对外可使用的 public 方法集合。因此,当前类直接声明的 public 方法,以及从父类或父接口继承的 public 方法,都属于这组 API 的搜索范围。

4.3 接口方法也属于搜索范围

getMethods() 不只搜索超类,还会包含从接口继承的 public 实例方法。不过,父接口声明的静态方法不会被继承,因此不会作为子接口或实现类的 public 方法被这组 API 返回。

在接口的 Class 对象上调用 getDeclaredMethods() 时,结果只包含这个接口直接声明的方法,包括:

  • 抽象方法
  • 默认方法
  • 静态方法
  • private 方法

它不包含父接口继承的方法。

对于普通类,getDeclaredMethods() 同样只关心当前类直接声明的方法。一个类为了实现接口而写下的方法,属于这个类自己声明的方法,因此会出现在该类的 getDeclaredMethods() 结果中。

五、构造器:没有继承这个维度

构造器与字段、方法最大的区别如下。

构造器继承规则(文本):

构造器不会被继承。

所以不存在“通过子类的 Class 对象获取父类构造器”这种情况。

这是因为构造器负责初始化“当前这个类”的实例,它不是可以被子类继承、重写或隐藏的方法。子类构造器可以通过 super(...) 调用父类构造器,但这不等于继承了父类构造器。因此,反射查找构造器时只能从声明它的类上查找。

API查找范围
getConstructor(parameterTypes...)当前类声明的指定 public 构造器
getDeclaredConstructor(parameterTypes...)当前类声明的指定构造器,不限制访问级别
getConstructors()当前类声明的全部 public 构造器
getDeclaredConstructors()当前类声明的全部构造器,不限制访问级别

构造器查找示例(Java):

Constructor<Child> publicConstructor =
        Child.class.getConstructor();

Constructor<Child> privateConstructor =
        Child.class.getDeclaredConstructor(String.class);

即使 Parent 有一个 public 无参构造器,它也不会出现在 Child.class.getConstructors() 中。

查找指定构造器失败时同样会抛出 NoSuchMethodException,因为构造器和方法的精确查找都使用这个异常类型。

六、单数 API 与复数 API

除了有没有 Declared,还要注意方法名的单复数。

类型示例行为
单数 APIgetField()getMethod()getConstructor()精确查找一个成员,找不到时抛出异常
复数 APIgetFields()getMethods()getConstructors()枚举范围内的成员并返回数组,没有结果时返回空数组

方法和构造器的单数 API 必须提供准确的参数类型。

基本类型参数查找(Java):

clazz.getMethod("setAge", int.class);

下面这行不能匹配参数为基本类型 int 的方法。

包装类型不等价示例(Java):

clazz.getMethod("setAge", Integer.class);

因为 int.classInteger.class 是不同的 Class 对象,反射的精确查找不会替你做自动装箱匹配。

七、返回数组没有固定顺序

下面这些复数 API 返回的数组都不承诺固定顺序:

  • getFields() / getDeclaredFields()
  • getMethods() / getDeclaredMethods()
  • getConstructors() / getDeclaredConstructors()

因此不要依赖数组下标表达成员含义。

错误的顺序假设(Java):

Field[] fields = clazz.getDeclaredFields();
Field first = fields[0]; // 不要假设它永远是源码中的第一个字段

如果业务需要稳定顺序,应根据明确规则自行排序,例如按名称排序。

按名称稳定排序(Java):

Arrays.sort(
        fields,
        Comparator.comparing(Field::getName)
);

八、API 选择清单

可以按下面的顺序判断。

API 选择流程(文本):

只需要 public 成员吗?
├─ 是
│  ├─ 需要查找继承的字段或方法:getXxx() / getXxxs()
│  └─ 查找构造器:getConstructor(s)()
└─ 否,需要当前类声明的非 public 成员
   └─ getDeclaredXxx() / getDeclaredXxxs()

需要父类声明的非 public 成员吗?
└─ 需要:先取得父类 Class,再对父类调用 getDeclaredXxx()

常见需求可以直接对应到 API:

需求选择
获取当前类和父类的 public 方法getMethods()
获取当前类自己声明的全部方法getDeclaredMethods()
获取父类声明的 private 字段父类 ClassgetDeclaredField()
获取当前类的 public 构造器getConstructors()
获取当前类的全部构造器getDeclaredConstructors()
按名称和参数类型找一个 public 方法getMethod()
按名称和参数类型找当前类的 private 方法getDeclaredMethod()

⚠️ 选对查找 API,不代表已经取得访问权限
getDeclaredXxx() 可以找到当前类声明的非 public 成员,但后续读取字段、调用方法或创建对象时仍会执行访问检查。“能找到”与“能访问”是两个步骤;访问控制的具体处理放在 R02 中讨论。

九、常见误区

误区正确认知
getDeclaredXxx() 能获取继承链上的全部成员它只获取当前类直接声明的成员
getXxx() 会获取当前类的全部成员它只获取 public 成员
子类可以通过反射取得父类构造器构造器不继承,只能从声明它的类上获取
找到 private 成员后就能直接操作找到成员与通过访问检查是两个步骤
getDeclaredMethods() 的结果顺序等于源码顺序反射 API 不保证返回顺序
int.classInteger.class 可以互换匹配精确查找时二者是不同的参数类型
getMethod("run") 只查当前类它还会查找继承体系中的 public 方法

十、总结

getXxx()getDeclaredXxx() 的差异,本质上是“可见性范围”和“声明范围”的差异:

规则getXxx()getDeclaredXxx()
public 成员获取获取当前类直接声明的
当前类非 public 成员不获取获取
继承的 public 字段和方法获取不获取
继承的非 public 成员不获取不获取
构造器只获取当前类的 public 构造器获取当前类声明的全部构造器

最终记忆(文本):

getXxx:public,并沿继承关系搜索字段和方法。
getDeclaredXxx:只看当前类直接声明,但不限制访问级别。
构造器不继承,所以永远只从当前类中查找。

总结 

到此这篇关于Java中getXxx()和getDeclaredXxx()到底有什么区别的文章就介绍到这了,更多相关Java中getXxx() 和getDeclaredXxx()区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于webLucene 安装方法

    关于webLucene 安装方法

    webLucene是一个基于开源项目lucene实现站内搜索的工具,关于它的安装,百度得到的大多是一样的,按照步骤也能正确安装并运行,需要注意的问题是
    2009-06-06
  • Spring boot实现应用打包部署的示例

    Spring boot实现应用打包部署的示例

    本篇文章主要介绍了Spring boot实现应用打包部署的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Java嵌套类和内部类详解

    Java嵌套类和内部类详解

    这篇文章主要介绍了Java嵌套类和内部类详解,本文讲解了什么是嵌套类及内部类、静态嵌套类、在外部类中定义内部类、在方法中定义内部类、匿名内部类等内容,需要的朋友可以参考下
    2015-04-04
  • Java运行时多态性的实现

    Java运行时多态性的实现

    Java运行时多态性的实现...
    2006-12-12
  • spring-data-redis 2.0 的使用示例代码

    spring-data-redis 2.0 的使用示例代码

    这篇文章主要介绍了spring-data-redis 2.0 的使用示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • Java位运算知识点详解

    Java位运算知识点详解

    这篇文章给大家分享了关于Java位运算的相关知识点内容,有兴趣的朋友们可以学习参考下。
    2018-09-09
  • MyBatis实现分页全过程

    MyBatis实现分页全过程

    MyBatis分页有三种方式:手动SQL分页(性能高但需处理数据库差异)、RowBounds(简单但不推荐)、PageHelper分页插件(自动改写SQL,推荐生产环境),PageHelper适合百万级数据优化,是终极解决方案
    2025-08-08
  • java开发RocketMQ消息中间件原理基础详解

    java开发RocketMQ消息中间件原理基础详解

    最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • SpringBoot返回对象时,如何将Long类型转换为String

    SpringBoot返回对象时,如何将Long类型转换为String

    这篇文章主要介绍了SpringBoot返回对象时,实现将Long类型转换为String,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 基于java SSM springboot实现抗疫物质信息管理系统

    基于java SSM springboot实现抗疫物质信息管理系统

    这篇文章主要介绍了基于JAVA SSM springboot实现的抗疫物质信息管理系统,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08

最新评论