1什么是反射?
Reflection(反射)是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
2为什么需要反射?
如果想创建对象,我们直接newUser();不是很方便嘛,为什么要去通过反射创建对象呢?
那我要先问你个问题了,你为什么要去餐馆吃饭呢?例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好?那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢?我们翘起二郎腿直接拿过来吃就好了。再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。
我们在后面的学习中,会学习框架,有一个框架Spring就是一个非常专业且功能强大的产品,它可以帮我们创建对象,管理对象。以后我无需手动new对象,直接从Spring提供的容器中的Beans获取即可。Beans底层其实就是一个MapString,Object,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。
总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。
3反射需要用到的API
3.1获取字节码对象
Class.forName(“类的全路径”);类名.class对象.getClass();
3.2常用方法
获取包名类名clazz.getPackage().getName()//包名clazz.getSimpleName()//类名clazz.getName()//完整类名获取成员变量定义信息getFields()//获取所有公开的成员变量,包括继承变量getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量getField(变量名)getDeclaredField(变量名)获取构造方法定义信息getConstructor(参数类型列表)//获取公开的构造方法getConstructors()//获取所有的公开的构造方法getDeclaredConstructors()//获取所有的构造方法,包括私有getDeclaredConstructor(int.class,String.class)获取方法定义信息getMethods()//获取所有可见的方法,包括继承的方法getMethod(方法名,参数类型列表)getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法getDeclaredMethod(方法名,int.class,String.class)反射新建实例clazz.newInstance();//执行无参构造创建对象clazz.newInstance(,”海绵宝宝”);//执行含参构造创建对象clazz.getConstructor(int.class,String.class)//获取构造方法反射调用成员变量clazz.getDeclaredField(变量名);//获取变量clazz.setAccessible(true);//使私有成员允许访问f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给nullf.get(实例);//访问指定实例变量的值,静态变量,第一参数给null反射调用成员方法Methodm=Clazz.getDeclaredMethod(方法名,参数类型列表);m.setAccessible(true);//使私有方法允许被调用m.invoke(实例,参数数据);//让指定实例来执行该方法
4反射的应用
4.1创建:测试物料类
创建包:cn.tedu.reflection创建类:Student.java
packagecn.tedu.reflection;/**本类用于测试反射,先准备的物料类*/publicclassStudent{//1.定义成员变量 Stringname;intage;//2.定义构造方法//快捷方式:右键--Source--倒数第三个--OKpublicStudent(){}//注意要先手动添加无参构造,防止被覆盖publicStudent(Stringname,intage){super();this.name=name;this.age=age;}//3.定义成员方法publicvoideat(intn){ System.out.println(饿了吃点火锅吧+n);}//4.提供重写的toString()//目的:为了方便查看对象的属性值
OverridepublicStringtoString(){returnStudent[name=+name+,age=+age+];}}4.2练习:获取类对象创建包:cn.tedu.reflection创建类:Test1Reflect.java
packagecn.tedu.reflection;importjava.lang.reflect.Constructor;importjava.util.Arrays;importorg.junit.Test;/**本类用来测试反射*/publicclassTest1Reflect{//1.创建入口函数main()--不用/**单元测试方法:是java测试的最小单位,使用灵活,推荐使用*语法要求:
Test+void+没有参数+public*注意使用时需要导包:AddJUnit4librarytothebuildpath:importorg.junit.Test;*单元测试方法执行方式:选中方法名--右键运行(RunAs--JUnitTest)--出现小绿条说明执行成功*///2.通过单元测试来测试如何获取类对象TestpublicvoidgetClazz()throwsException{/**右键要获取字节码对象的类名,选择CopyQuailfiedName复制类的全路径名*/ Class?student1=Class.forName(cn.tedu.reflection.Student);//此处的参数是类的全路径名[包名+类名] Class?student2=Student.class; Class?student3=newStudent().getClass();//先创建匿名对象,匿名对象没有名字,然后对象的字节码对象 System.out.println(student1);//反射得到的字节码Class对象 System.out.println(student2.getName());//获取类的全路径名[包名+类名] System.out.println(student3.getSimpleName());//只获取类名 System.out.println(student3.getPackage().getName());//获取包名}}4.3练习:类获取构造方法packagecn.tedu.reflection;importjava.lang.reflect.Constructor;importjava.util.Arrays;importorg.junit.Test;/**本类用来测试反射*/publicclassTest1Reflect{//3.通过单元测试来测试如何获取构造方法
TestpublicvoidgetConstruct(){//1.获取字节码Class对象 Class?clazz=Student.class;//2.获取构造方法们,并放入cs数组中 Constructor?[]cs=clazz.getConstructors();//3.获取每个构造//使用增强for循环完成//for(12:3){循环体}其中3是要遍历的数据,1是遍历后每个元素的数据类型2是遍历后每个数据的变量名for(Constructorc:cs){//拿到每个构造方法以后可以获取构造方法的相关信息 System.out.println(c.getName());//获取构造方法的名称 Class[]cp=c.getParameterTypes();//获取构造方法的参数类型,可能有多个 System.out.println(Arrays.toString(cp));}}}4.4练习:获取成员方法packagecn.tedu.reflection;importjava.lang.reflect.Method;importjava.util.Arrays;importorg.junit.Test;/**本类用来测试反射*/publicclassTest1Reflect{//4.通过单元测试来测试获取成员方法
TestpublicvoidgetFunction()throwsException{//1.获取Class字节码对象 Class?clazz=Class.forName(cn.tedu.reflection.Student);//2.获取所有成员方法 Method[]ms=clazz.getMethods();//3.遍历数组,获取每个方法的信息for(Methodm:ms){ System.out.println(m.getName());//获取方法名 Class?[]pt=m.getParameterTypes();//获取方法参数类型 System.out.println(Arrays.toString(pt));}}}4.5练习:获取成员变量packagecn.tedu.reflection;importjava.lang.reflect.Field;importorg.junit.Test;/**本类用来测试反射*/publicclassTest1Reflect{//5.通过单元测试来测试获取成员变量
TestpublicvoidgetFields(){//1.获取Class字节码对象/**Class?中的?是泛型约束的通配符,类似于**/ Class?clazz=Student.class;//2.获取所有的成员变量,公共的!!!/**!!!注意目前成员变量的修饰符必须是public才能获取到,采用默认修饰符就反射不到*/ Field[]fs=clazz.getFields();//3.遍历数组,获取每个成员变量的信息for(Fieldf:fs){ System.out.println(f.getName());//获取变量名 System.out.println(f.getType().getName());//获取变量类型}}}4.6练习:创建对象packagecn.tedu.reflection;importjava.lang.reflect.Constructor;importorg.junit.Test;/**本类用来测试反射*/publicclassTest1Reflect{//6.通过单元测试来测试反射创建对象/***方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象*方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance(),触发对应的构造函数来创建对象**/
TestpublicvoidgetObject()throwsException{//1.获取Class字节码对象 Class?clazz=Student.class;//2.创建对象 Objectobj=clazz.newInstance();//触发无参构造 System.out.println(obj);//Student[name=null,age=0]//3.可以指定去调用哪个构造方法,注意根据参数来指定,而且传入的是参数的字节码对象 Constructor?c=clazz.getConstructor(String.class,int.class);//4.触发指定的构造方法 Objectobj2=c.newInstance(海绵宝宝,3); System.out.println(obj2);//Student[name=海绵宝宝,age=3]//5.查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类类型/**为什么要把Object强转成子类类型?因为想要使用子类的特有功能,父类无法使用子类的特有功能*obj是Object类型,Object中没有Student的属性与功能*这个操作叫做向下转型--想使用子类特有功能时需要做此操作**/ Students=(Student)obj2; System.out.println(s.name); System.out.println(s.age); s.eat();}}4.7熟悉API自己创建类练习,获取类中的所有资源,熟悉反射中涉及的API
5暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下:
5.1创建:测试物料类
创建包:cn.tedu.reflection创建类:Person.java
packagecn.tedu.reflection;/**本类用来测试暴力反射*/publicclassPerson{//1.提供私有属性privateStringname;privateintage;//2.提供私有方法privatevoidsave(intm,Stringn){System.out.println(save()..+m+n);}privatevoidupdate(){System.out.println(update()..);}}5.2练习:创建测试类
创建包:cn.tedu.reflection创建类:TestReflect2.java
packagecn.tedu.reflection;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importorg.junit.Test;/**本类用于测试暴力反射*/publicclassTestReflect2{//1.通过单元测试来测试暴力反射获取和设置私有属性
TestpublicvoidgetField()throwsException{//1.获取Class字节码对象 Class?clazz=Person.class;//2.获取私有属性 Fieldfield=clazz.getDeclaredField(name);//3.根据获取到的属性对象获取属性类型 System.out.println(field.getType().getName());//4.设置属性的值//set(m,n)--m:要给哪个对象设置值,n:是要设置的具体值//4.1没有对象就通过反射的方式创建对象 Objectobj=clazz.newInstance();//4.2暴力反射!!!设置私有可见 field.setAccessible(true);//4.3给指定对象设置值 field.set(obj,派大星);//5.获取私有属性的值 System.out.println(field.get(obj));}//2.通过单元测试来测试暴力反射获取和设置私有方法TestpublicvoidgetFunction()throwsException{//1.获取Class字节码对象 Class?clazz=Person.class;//2.通过暴力反射获取私有方法//getDeclaredMethod(m,n,x,y....)m:要执行的方法名nxy:是方法的参数类型 Methodmethod=clazz.getDeclaredMethod(save,int.class,String.class);//3.通过暴力反射执行私有方法//invoke(m,n,x,y...)m:要执行的哪个对象的方法nxy:是方法的参数类型//3.1没有对象就通过反射的方式创建对象 Objectobj=clazz.newInstance();//3.2想要执行私有方法,首先设置私有可见 method.setAccessible(true);//3.3invoke表示通过反射执行方法 method.invoke(obj,,蟹老板);}}恭喜你,又学会了一个新知识,反射的API比较多,要多多练习哦下一节内部类点这里哦
,