JAVA Class类与反射

JAVA Class类与反射关于Class类与Class对象与反射Class类与Class对象Class对象是Class类的实例,类至少包含以下信息,因此class类又可以被解构为如下部分:权限修饰符类名参数化类型(泛型信息)接口Interface注解Annotation字段Field(重点)构造器Constructor(重点)方法Methd(重点)以下图为例:整个.class文件最终都成为字节数组byte[]b,里面的构造器、方法等各个“组件”,其实也是字节。打开Class类的源代码,发现果然如此:

大家好,又见面了,我是你们的朋友全栈君。

Class类与Class对象

Class对象是Class类的实例,类至少包含以下信息,因此class类又可以被 解构为如下部分:

  • 权限修饰符
  • 类名
  • 参数化类型(泛型信息)
  • 接口Interface
  • 注解Annotation
  • 字段Field(重点)
  • 构造器Constructor(重点)
  • 方法Methd(重点)

以下图为例:
在这里插入图片描述

整个.class文件最终都成为字节数组byte[] b,里面的构造器、方法等各个“组件”,其实也是字节。
打开Class类的源代码,发现果然如此:

private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }

而且,针对字段、方法、构造器,因为信息量太大了,JDK还单独写了三个类,比如

Method类:

在这里插入图片描述

Field类:

在这里插入图片描述

Constructor类:

在这里插入图片描述

也就是说,Class类准备了很多东西来标识一个.class文件的信息,并写了三个类,Method,Fileld,Constructor来描述方法、字段、构造器等信息。
比如:
在这里插入图片描述

也就是说,Class类准备了很多字段用来表示一个.class文件的信息,对于字段、方法、构造器等,为了更详细地描述这些重要信息,还写了三个类,每个类里面都有很详细的对应

理解反射API

没啥好说的,在日常开发中反射最终目的主要两个:

  • 创建实例
  • 反射调用方法

创建实例的难点在于,很多人不知道clazz.newInstance()底层还是调用Contructor对象的newInstance()。所以,要想调用clazz.newInstance(),必须保证编写类的时候有个无参构造。

反射调用方法的难点,有两个,初学者可能会不理解。

在此之前,先来理清楚Class、Field、Method、Constructor四个对象的关系:
在这里插入图片描述

Field、Method、Constructor对象内部有对字段、方法、构造器更详细的描述。

  • 难点一:为什么根据Class对象获取Method时,需要传入方法名+参数的Class类型
    在这里插入图片描述

为什么要传 nameParameterType
因为.class文件中有多个方法,比如:
在这里插入图片描述

所以必须传入name,以方法名区分哪个方法,得到对应的Method。

那参数parameterTypes为什么要用Class类型,我想和调用方法时一样直接传变量名不行吗,比如userName, age。

答案是:我们无法根据变量名区分方法

实际上,调用Class对象的getMethod()方法时,内部会循环遍历所有Method,然后根据方法名和参数类型匹配唯一的Method返回。

private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

难点二:调用method.invoke(obj, args);时为什么要传入一个目标对象?

上面分析过,.class文件通过IO被加载到内存后,JDK创造了至少四个对象:Class、Field、Method、Constructor,这些对象其实都是0101010的抽象表示。

以Method对象为例,它到底是什么,怎么来的?我们上面已经分析过,Method对象有好多字段,比如name(方法名),returnType(返回值类型)等。也就是说我们在.java文件中写的方法,被“解构”以后存入了Method对象中。所以对象本身是一个方法的映射,一个方法对应一个Method对象。

对象的本质就是用来存储数据的。而方法作为一种行为描述,是所有对象共有的,不属于某个对象独有。比如现有两个Person实例

    Person p1 = new Person();
    Person p2 = new Person();

对象 p1保存了”hst”和18,p2保存了”cxy”和20。但是不管是p1还是p2,都会有changeUser(),但是每个对象里面写一份太浪费。既然是共性行为,可以抽取出来,放在方法区共用。

但这又产生了一个棘手的问题,方法是共用的,JVM如何保证p1调用changeUser()时,changeUser()不会跑去把p2的数据改掉呢?

所以JVM设置了一种隐性机制,每次对象调用方法时,都会隐性传递当前调用该方法的对象参数,方法可以根据这个对象参数知道当前调用本方法的是哪个对象!

示例图

同样的,在反射调用方法时,本质还是希望方法处理数据,所以必须告诉它执行哪个对象的数据。

在这里插入图片描述

所以,把Method理解为方法执行指令吧,它更像是一个方法执行器,必须告诉它要执行的对象(数据)。

当然,如果是invoke一个静态方法,不需要传入具体的对象。因为静态方法并不能处理对象中保存的数据。

参考文章:
【1】https://www.zhihu.com/question/24304289/answer/694344906

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/145162.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月17日 下午10:40
下一篇 2022年5月17日 下午10:40


相关推荐

  • 【致远FAQ】致远OA宕机之Tomcat异常宕机

    【致远FAQ】致远OA宕机之Tomcat异常宕机问题描述tomcat进程已经不在; 由于在启动命令行参数中增加了相关的日志监控,重点查找JVM内存溢出、jvm的crash的日志进行问题定位; 没有.hprof文件生成【基本可以推论没有出现JVM内存溢出】 没有hs_err_xxx.log文件生成【基本可以推论JVM没有出现严重的crash异常】问题分析1)通过catalina.log看出tomcat出现了非正常关闭操作下的停机;如果是正常停机会在输出图1的日志前输出如图2所示的内容2)tomcat停机的时..

    2022年7月23日
    14
  • GitLab新建分支和删除分支

    GitLab新建分支和删除分支1 新建分支

    2026年3月19日
    3
  • hbase splits_hbase shell scan

    hbase splits_hbase shell scan1Region拆分一个Region代表一个表的一段Rowkey的数据集合,当Region太大,Master会将其拆分。Region太大会导致读取效率太低,遍历时间太长,通过将大数据拆分到不同机器上,分别查询再聚合,Hbase也被人称为“一个会自动分片的数据库”。Region可以手动和自动拆分。1.1Region自动拆分1.1.1ConstantSizeRegionSplitPo…

    2025年9月30日
    4
  • typescript教程阮一峰_在博图里怎样定义一个变量

    typescript教程阮一峰_在博图里怎样定义一个变量在TypeScript项目中像PHP一样使用魔术变量

    2022年4月20日
    114
  • dhtml gantt所有配置_dhtmlxGantt

    dhtml gantt所有配置_dhtmlxGanttdhtmlxGantt 交互式的 JavaScript HTML5 甘特图软件商 XBSoftware 当前版本 7 0 5 发布日期 2020 6 4 推荐 以下是 新版功能 如果您需要了解更多信息 您可以联系我们 v7 0 52020 年 6 月 19 日错误修复发布当 duration unitconfig 设置为 小时 时 工作时间计算的性能改进 当 duration unitconfig 设置为

    2026年3月19日
    2
  • 网易发布安全skill,为ClawHub生态提供前置安全防护

    网易发布安全skill,为ClawHub生态提供前置安全防护

    2026年3月14日
    2

发表回复

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

关注全栈程序员社区公众号