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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Docker安装应用程序(Centos6.5_x64)

    Docker安装应用程序(Centos6.5_x64)

  • 机器学习:回归问题

    机器学习:回归问题回归,我第一次看到回归的时候,想的就是回归是什么意思?后来看了一个答案解释很有意思,回归这个词来自于生物学,在调查父母与子代身高问题的时候,发现父母如果过高的话,子女就会比父母矮一点,如果父母矮的话,

  • phpstorm2021.3激活码(在线激活)

    phpstorm2021.3激活码(在线激活),https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 爱比较病毒测试(不是震网病毒特点的是)

    CIH、爱虫、冲击波、熊猫烧香,对这4种网络病毒你了解多少?现在的网络病毒能够分为三类,第一类是“蠕虫”病毒,这类型的病毒是在感染电脑后经过不停地复制自己,将电脑原来的运行和储存资源耗费掉,最终电脑由于资源耗费干净而无法运行直至死机;第二类是“进犯型”病毒,这类病毒和蠕虫病毒不一样,进犯型病毒一旦感染就会对电脑软件甚至是硬件做出破坏性的进犯,威力和破坏性极端强大。最终第三类是“木马”类病毒,这类病…

  • MySql jdbc autoReconnect 的应用[通俗易懂]

    MySql jdbc autoReconnect 的应用[通俗易懂]http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html

  • 安卓手机修改ntp服务器,修改安卓手机ntp服务器地址「建议收藏」

    安卓手机修改ntp服务器,修改安卓手机ntp服务器地址「建议收藏」修改安卓手机ntp服务器地址内容精选换一换TTL(Time-To-Live)指解析记录在本地DNS服务器中的缓存时间。本地DNS服务器指用户客户端(手机、电脑等)连接Internet网络使用的DNS,默认使用的DNS是宽带运营商自动分配的DNS服务器,用户也可以将该DNS修改为公共DNS服务器,例如,114.114.114.114、8.8.8.8。通过华为云购买的弹性云服务器默认硬件要求如表1所…

发表回复

您的电子邮箱地址不会被公开。

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