当前位置: 首页 > 科技观察

说说Java知识点的反射

时间:2023-03-22 11:03:49 科技观察

前言今天先说说Java模块的内容:反射。反射介绍一般情况下,我们知道自己要操作哪些类和对象,可以直接操作这些对象中的变量和方法,比如一个User类:Useruser=newUser();user.setName("Bob");但是在某些场景下,我们无法正常操作:只知道类路径,不能直接实例化对象。对象的变量和方法不能被直接操作,比如私有方法和私有变量。需要hook系统逻辑,比如修改一个实例的参数。等等。所以我们需要一种允许我们操作任意类和对象的机制。这种机制就是反射。简单的说,反射就是:对于任何一个类,你都可以知道这个类的所有属性和方法;对于任何对象,您都可以调用它的任何方法和属性。常用API示例首先设置一个User类:packagecom.example.testapplication.reflection;publicclassUser{privateintage;publicStringname;publicUser(){System.out.println("CalledUser()");}privateUser(intage,Stringname){this.name=name;this.age=age;System.out.println("CalledUser(age,name)"+"__age:"+age+"__name:"+name);}publicUser(Stringname){this.name=name;System.out.println("CalledUser(name)"+"__name:"+name);}privateStringgetName(){System.out.println("调用了getName()");returnthis.name;}privateStringsetName(Stringname){this.name=name;System.out.println("CalledsetName(name)__"+name);returnthis.name;}publicintgetAge(){System.out.println("CalledgetAge()");returnthis.age;}}获取Class对象主要有3种方法:根据类路径获取类对象,直接获取实例对象的getclass方法//1.根据类路径获取class对象try{Classclz=Class.forName("com.example.testapplication.reflection.User");}catch(ClassNotFoundExceptione){e.printStackTrace();}//2,直接获取Classclz=用户。类;//3。对象的getclass方法Classclz=newUser().getClass();获取类的构造函数1.获取类的所有构造函数Classclz=User.class;//获取所有的构造函数(不包括私有构造函数)Constructor[]constructors1=clz.getConstructors();//获取所有的构造函数(包括私有构造函数)Constructor[]constructors2=clz.getDeclaredConstructors();2.获取一个类的单个构造函数其参数为int和StringClass[]params={int.class,String.class};Constructorconstructor3=clz.getDeclaredConstructor(params);}catch(NoSuchMethodExceptione){e.printStackTrace();}需要注意的是User(intage,Stringname)是私有构造函数,所以需要使用getDeclaredConstructor获取调用类的构造函数生成实例对象1.调用Class对象的newInstance方法。该方法只能调用无参构造函数,即Class对象的newInstance方法不能传入参数。对象用户=clz.newInstance();2。调用Constructor对象的newInstance方法Class[]params={int.class,String.class};Constructorconstructor3=clz.getDeclaredConstructor(params);constructor3.setAccessible(true);constructor3.newInstance(22,"Bob");这里需要注意的是,getDeclaredConstructor虽然可以获取私有构造方法,但是如果要调用这个私有方法,需要设置setAccessible(true)方法,否则会报错:cannotaccessamemberofclasscom.example.testapplication.reflection.Userwithmodifiers"private"获取类的属性(包括私有属性)Classclz=User.class;Fieldfield1=clz.getField("name");Fieldfield2=clz.getDeclaredField("age");同样,getField获取公共类变量,getDeclaredField可以获取所有变量(包括私有变量属性)。所以一般直接使用getDeclaredField即可。修改实例的属性继续前面的例子,获取到类的属性后,就可以修改类实例的相应属性了。例如,如果我们有一个用户的实例对象,让我们修改它的名字和年龄。//修改name,name为公共属性Classclz=User.class;Fieldfield1=clz.getField("姓名");field1.set(user,"xixi");//修改年龄,年龄为私有属性Classclz=User.class;Fieldfield2=clz.getDeclaredField("age");field2.setAccessible(true);field2.set(user,123);获取类的方法(包括私有方法)//获取getName方法Methodmethod1=clz.getDeclaredMethod("getName");//获取带参数的setName方法Methodmethod2=clz.getDeclaredMethod("setName",String.class);//获取getage方法Methodmethod3=clz.getMethod("getAge");调用实例方法method1.setAccessible(true);Objectname=method1.invoke(user);method2.setAccessible(true);method2.invoke(user,"xixi");Objectage=method3.invoke(user);反射优缺点虽然反射非常有用,增加了程序的灵活性,但是它也有它的缺点:性能问题。由于使用了动态类型(运行时检查类型),反射的效率比较低。但是对程序的影响比较小,除非对性能要求比较高。因此需要在两者之间取得平衡。不够安全。因为可以实现一些私有的属性和方法,所以可能会引起安全问题。难读难写。当然,也有解决方案,比如jOOR库,但不适用于Android定义为final的字段。AndroidHook技术中的应用插件(Hook)也称为钩子函数。在系统调用函数之前,钩子程序会先捕获消息,钩子函数会先拿到控制权。这时钩子函数可以处理(改变)这个函数的执行行为,也可以强行结束消息的传递。在插件中,我们需要找到一个可以hook的点,然后做一些插件的工作,比如替换Activity,替换mH等等。这里用到了很多反射的知识,这里有一个替换mH的例子://获取当前ActivityThread对象sCurrentActivityThread");currentActivityThreadField.setAccessible(true);ObjectcurrentActivityThread=currentActivityThreadField.get(null);//获取对象的mHFieldmHField=activityThreadClass.getDeclaredField("mH");mHField.setAccessible(true);HandlermH=(HandlermH=(mHField.get(currentActivityThread);//把mh换成我们自己的HandlerCallbackFieldmCallBackField=Handler.class.getDeclaredField("mCallback");mCallBackField.setAccessible(true);mCallBackField.set(mH,newMyActivityThreadHandlerCallback(mH));动态代理dynamic的特性agent的特点在于不需要事先创建agent对象,而是在运行时使用反射机制创建agent类,从而实现动态代理功能莉莉。publicclassInvocationTestimplementsInvocationHandler{//代理对象(代理接口)privateObjectsubject;publicInvocationTest(Objectsubject){this.subject=subject;}@OverridepublicObjectinvoke(Objectobject,Methodmethod,Object[]args)throwsThrowable{//代理前的真实对象Objectobj=method.invoke(subject,args);//代理后返回objrealobject;}}三方库(注解)我们可以发现很多库都使用了注解,获取注解的过程也会有一个反射过程,比如获取Activity中所有变量的注解:publicvoidgetAnnotation(Activityactivity){Classclazz=activity.getClass();//获取activity中的所有变量Field[]fields=clazz.getDeclaredFields();for(Fieldfield:fields){field.setAccessible(true);//获取添加到的注解变量MyAnnotationtest=field.getAnnotation(MyAnnotation.class);//...}}这种通过反射处理注解的方法称为运行时注解,即只在程序运行时才处理注解。但是正如上面所说,反射会在一定程度上影响程序的性能,所以还有另外一种处理注解的方法:编译期注解。使用的注解处理工具是APT。APT是一个注解处理器,可以在编译时扫描并处理注解,然后生成java代码文件。与反射相比,这种方式可以对程序的运行性能产生比较小的影响。APT的使用这里就不说了,下次有专门的章节讲一下~Android架构Android架构:https://github.com/JiMuzz/Android-架构参考https://www.jianshu。com/p/3382cc765b39https://segmentfault.com/a/1190000015860183本文转载自微信公众号“代码积木”,可通过以下二维码关注。转载本文请联系代码上的积木公众号。