反射的作用


前言

反射,可以理解为镜子,只要镜子的位置摆的好,就可以绕过某些面前的东西,让我们看到其背后的东西。java的反射也是一样的道理,绕过某些东西,得到其背后的东西,接下来就带大家理解一下,反射绕过的是什么,得到的是什么。


一、反射是什么?

1、反射是一种可以无视权限,暴力访问类的变量和实例方法的一种手段
2、反射是一种可以跳过泛型检查,往集合里面添加任意数据的手段
3、反射是一种,只要知道类名,就可以得到它所有属性、方法、构造器,是所有噢,不论是不是私有。

二、反射凭啥可以做这些

这里就要说一下反射的工作原理。如图所示:
在这里插入图片描述
我们编写一个类,到生成它的正常步骤应该是,编写完以后生成java源文件,java源文件通过编译,生成class文件。class文件在运行时,又会通过classLoader,将其加载到JVM中,而外部在创建对象,或者引用这个类时,就会在JVM中创建其对象。

注意:并不是所有的类都会加载到JVM中,某些不怎么用的,或者用的少的,只有在调用时,才会加载进JVM

正常情况下,想要看到一个类的属性及方法,都是通过JVM构建出来这个对象,才能看得到。而反射就像程咬金一样,半路就杀出来,从“娘胎”直接获得这个类,通过一系列小手段就可以获得这个类的所有信息了。

三、反射的“小手段”

1.引入单词

首先带大家认识几个java单词。
class :类
constructor :构造器
field : 字段,也就是属性、变量的意思
method : 方法
Declared : 声明
Accessible : 访问

2.引入方法

在这里插入图片描述

获取字节码文件

想要使用反射,首先就要先获取到字节码文件。获取谁的呐?就这么记:想要反射谁,就获取谁的字节码文件。

获取字节码文件主要有两种方式:
1、直接根据类名获取:类名.class
2、根据对象名来获取:对象.class

获取构造器

有了字节码文件,就可以获取其构造器了

获取构造器主要有两大类,四小类方式:
1、获取全部构造器(复数加s):
(1)getConstructors( ) :获取全部构造器,但只能是public类型的
(2)getDeclaredConstructors( ) :获取全部构造器,只要声明了就可以获取

2、获取单个构造器(单数没有s):
(1)getConstructor( 参数 ) : 获取某一个构造器,但只能是public类型的
(2)getDeclaredConstructor(参数) : 获取某一个构造器,只要声明了就可以获取

参数:对象的成员变量的数据类型.class
如:学生的name是String类型的,在正常的构造器中参数应该是 String name,而在这里,只用写这个变量的数据类型.class ,也就是String.class
有几个变量就写几个(必须按javaBean的顺序写~)不写参数默认获取无参构造器

提示:如果获取的构造器是私有的,就需要设置访问方式(也叫暴力反射):
暴力反射:setAccessible(true)
->参数默认为false,也就是不允许访问,改为true,也就是允许访问的意思。

获取到构造器后,就可以创造对象啦~
利用反射得到的构造器 调用newInstance方法
构造器.newInstance( )

获取成员变量

有了字节码文件,就可以获取其成员变量了

获取成员变量主要有两大类,四小类方式:
1、获取全部成员变量(复数加s):
(1)getFields( ) :获取全部成员变量,但只能是public类型的
(2)getDeclaredFields( ) :获取全部成员变量,只要声明了就可以获取

2、获取单个成员变量(单数没有s):
(1)getField( 参数 ) : 获取某一个成员变量,但只能是public类型的
(2)getDeclaredField(参数):获取某一个成员变量,只要声明了就可以获取

参数:就直接填变量名即可
如:学生的name是String类型的,这里的参数就是 “name”

提示:如果获取的成员变量是私有的,就需要设置访问方式(也叫暴力反射):
暴力反射:setAccessible(true)
->参数默认为false,也就是不允许访问,改为true,也就是允许访问的意思。

获取到成员变量后,就可以为其赋值了
成员变量.set(对象,变量值) =>赋值
如:name.set(student,“张三”)
成员变量.get(对象) =>取值
如:name.get(student)

获取成员方法

有了字节码文件,就可以获取其成员方法了

获取成员方法主要有两大类,四小类方式:
1、获取全部成员方法(复数加s):
(1)getMethods( ) :获取全部成员方法,但只能是public类型的
(2)getDeclaredMethods( ) :获取全部成员方法,只要声明了就可以获取

2、获取单个成员方法(单数没有s):
(1)getMethod( 参数 ) : 获取某一个成员方法,但只能是public类型的
(2)getDeclaredMethod(参数):获取某一个成员方法,只要声明了就可以获取

参数:参数有两个,第一个是方法名,第二个是方法的参数的数据类型.class
如:学生类里面有一个方法是:eat(String name){ }
在这里的参数应该写: (“eat”,String.class)

提示:如果获取的成员方法是私有的,就需要设置访问方式(也叫暴力反射):
暴力反射:setAccessible(true)
->参数默认为false,也就是不允许访问,改为true,也就是允许访问的意思。

获取到的方法想要执行,需要调用invoke( )方法
并且,invoke( )也有两个参数,第一个参数为实际对象,第二个参数为方法本身的参数
如:eat.invoke(student,“张三”)

四.反射有啥用呢?

给大家带来两个案例,举例说明其作用~

案例1:

可以无视权限,直接访问类的变量和实例方法

如图,定义了一个学生的javaBean,有构造方法、get、set方法、toString方法

在这里插入图片描述
普通创建对象,调用有参、无参构造器、调用get、set方法都是没问题的

在这里插入图片描述
但是,当我们修改javaBean的方法后,将所有构造器、方法都改成私有的

在这里插入图片描述
这种情况下,我们就无法使用这个对象了

在这里插入图片描述
我们看报错信息:无法访问到构造器、找不到参数等等

在这里插入图片描述
但是我们又需要这个对象咋办嘞~,这个时候反射:我可以 ~
在这里插入图片描述
代码如下(示例):

public static void main(String[] args) throws Exception {
        //反射:通过类,获得类加载器
        Class<Student> studentClass = Student.class;
        //通过类加载器获得构造器
        Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class, int.class);
        Constructor<Student> constructor1 = studentClass.getDeclaredConstructor();
        //因为构造器都是私有的,因此需要设置暴力反射
        constructor.setAccessible(true);
        constructor1.setAccessible(true);
        //通过构造器获得对象
        Student student = constructor.newInstance("张三", 18);
        Student student1 = constructor1.newInstance();


        //获得学生对象的set方法,给无参构造器创建的学生赋值
        Method setName = studentClass.getDeclaredMethod("setName", String.class);
        Method setAge = studentClass.getDeclaredMethod("setAge", int.class);

        //因为方法都是私有的,因此需要设置暴力反射
        setName.setAccessible(true);
        setAge.setAccessible(true);

        //给无参构造器创建的对象赋值
        setName.invoke(student1,"李四");
        setAge.invoke(student1,81);

        System.out.println("student = " + student);
        System.out.println("student1 = " + student1);
    }

尽管设置了私有,我们使用反射,依旧可以使用对象,并且赋值

在这里插入图片描述

案例2:

可以跳过泛型检查,往集合里面添加任意数据

我们都知道,定义了一个集合,给他规定泛型,就能约束其添加的元素类型
如下图所示:
在这里插入图片描述
这个集合的泛型是Integer类型的,只能添加整型数据,假如我们添加一个string类型的元素,就会报错

在这里插入图片描述

但是,反射:我可以~

在这里插入图片描述
代码如下(示例):

	List<Integer> list = new ArrayList<>();
        //通过反射调用list的类加载器
        Class<?> aClass = list.getClass();
        //通过类加载器获得add()方法
        Method method = aClass.getDeclaredMethod("add", Object.class);
        //通过反射得到的方法,强行赋值
        method.invoke(list,"张三");

        System.out.println("list = " + list);

然后我们发现,原本应该是整型的集合,多了一个张三,是不是很神奇,而且,原来的集合,并没有被改变结构噢~依旧只能添加整型数据。

在这里插入图片描述

总结

充电时刻
今天只是带了两个很小的案例来说明反射的作用,其实反射离我们既遥远,又近在眼前。遥远在:我们并不需要去写这些代码,学习反射也只是了解其作用。近在:反射被广泛应用在框架上,我们每天接触的spring就涉及了反射,只是底层给我们封装好了而已。

而且反射,听起来很难,其实很简单的,和平时创建对象的用法是一样滴~ 只是细分出来了四个步骤,而且每个步骤都是类似的,把单词记住了就可以学会反射啦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值