46 反射
反射,被称为框架设计的灵魂。
框架:半成品的软件,开发效率高,可以简化代码
概念:将类的各组成部分封装成其他对象,这就是反射机制
1我们知道java代码在计算机中有三个阶段,如上图,第一阶段源代码阶段,。java代码写好后javac运行编译成字节码.class文件,这时代码是在计算机硬盘中,第三阶段是我们熟悉的运行阶段,创建对象,new之后调用属性方法,而第二阶段是将硬盘文件读取到内存中,是通过java的ClassLoader傀儡实现加载,将成员变量封装成Field,然后多成员变量即使用数组,构造方法封装成Constructor,成员方法封装成Method,其实这就是一种反射机制
反射的优点,可以在程序运行时调用对象,可以解耦,增加模块扩展性
为了获取类对象有3种方式:
1编译阶段,未加载到内存时,Class.forName("类名")将类加载到内存
2已经在内存中,使用 类名.class获取类对象
3运行阶段,有new出来的实例,使用实例.getClass()获取类对象,此方法继承自Object
2如上,我们分别使用了3种方法实现获取类对象,其中因为forName方法要抛出异常,我们直接使用了基类Exception,类名字符串必须是包名拼接,我们可以看到3种方法输出一样,同时==来比较这些类对象是否是同一个对象,可以得出结论,class在程序当次运行时只会加载一次(其实Class默认还有泛型,但是我们右侧是指定类型,所以左侧可以省略)
三种方法,Class.forName方法多用于配置文件,读取文件,加载类
第二种用于传参,第三种只是利用对象获取字节码class
Class对象的各种获取方法
3我们获得了字节码对象后,如之前讲的,可以通过各种方法获取成员变量,构造方法,成员方法,还有类名,对应还有获取所有和获取单个的,还有获取一般和Declared的,这个后面讲讲Declared是干嘛用的,我们功能分别讲解
获取Field成员变量
4还是针对上次使用的Person类,我们定义了无参和有参的构造,getter和setter,还设置了toString方法
Field[] getFields()方法
5如上,获取类就用Person.class,我们遍历Fields数组,打印发现什么都没有,所以getFields[]返回的其实是public属性
6如上,我们添加几个公有的public属性,可以被打印出来
7为了进一步确定,我们添加默认和protected属性,结果一致
Field getField(String name)方法 根据指定public属性名获取Field方法
8如上,我们定义了a为public属性,那么就可以获取,如上可以打印出来,但是我们获取到Field对象有什么用呢,
9我们查看文档可以看到Field成员方法,get和set方法,get获取属性值,set给属性设置值,因此我们获取到对象和类就可以给其设置值
10代码如上,为了给参数传入obj,我们实例对象p,a属性默认是int为0,我们设置值后变成33
Field[] getDeclaredFields()方法
11我们运行可以发现,所有成员变量都打印了出来,不论是public,protected,默认,private
Field getDeclaredField()方法
同样,你会认为其是获得不受限的成员属性,其实访问private时报错
12如上,因为私有属性本身还是有保护性的,但是也不是不能访问到
13我们这里需要使用暴力反射Field对象的setAccessible(Boolean)方法,设置为true,我们就可以访问
获取Constructor构造方法
14如上,我们使用getConstructor方法获取Constructor构造器对象,我们可以指定无参或者有参,有参数的时候,需要使用对应类型的class对象,我们获取到Constructor构造器对象,需要关注的方法就是其newInstance方法,创建实例,我们可以将其打印查看结果
15当然,视频里还说无参情况下可以直接使用class对象的newInstance方法,但是IDE给标横线证明是废弃了,至于获取构造器的其他3个方法,很少用,如果有私有的构造方法,我们记得要使用setAccessible来解决调用问题
获取Method成员方法
16我们先运行class的getMethods(),可以遍历看到打印的是所有的public方法,包括继承自父类Object的方法
17我们给Person添加2个eat方法,一个无参,一个传入字符串
18使用getMethod,可以指定对象名,我们尝试打印这个Method对象如上
当然,我们获得Method成员方法,是希望能运行,而不只是打印函数名Method对象,有方法invoke(Object obj,...参数列表)
19如上,我们就分别实现了调用对象的2个eat方法
当然Method对象还有getName获得方法名
反射案例
我们写了很多反射相关的,感觉有的是很墨迹,没有体验到其优秀之处,这里一个案例展示(是黑马视频的案例,要求根据用户配置好的文件,实现创建配置文件指定类,并使用其方法,视频里会说虽然我们现在有Person类,但是还可能有Student类啊,还可能有其他方法啊巴拉巴拉。。)
20视频里介绍配置文件使用.properties后缀存,为什么呢,后续讲,里面写上我们配置的类名和方法名,=连接右侧无引号,如上即编写完毕,其中类名要加上包名
21如上,需要注意的是使用Properties类,可以使用load方法,加载结果存储类似map结构,要求传入的是inputStream,我们可以实例输入流,但是其实我们有类加载器,使用类加载器可以使用其getResourceAsStream获得,pro对象load就可以使用getProperty获得=右侧值,我们就获得了类名和方法名,其他调用就没什么说的了(我们查看框架的配置文件,有时候看见类名,就可以知道使用的是反射机制)