前言
反射,可以理解为镜子,只要镜子的位置摆的好,就可以绕过某些面前的东西,让我们看到其背后的东西。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就涉及了反射,只是底层给我们封装好了而已。
而且反射,听起来很难,其实很简单的,和平时创建对象的用法是一样滴~ 只是细分出来了四个步骤,而且每个步骤都是类似的,把单词记住了就可以学会反射啦~