前言

创新互联公司-成都网站建设公司,专注成都做网站、成都网站建设、网站营销推广,域名与空间,网页空间,绵阳服务器托管有关企业网站制作方案、改版、费用等问题,请联系创新互联公司。
今天说Java模块内容:反射。
反射介绍
正常情况下,我们知晓我们要操作的类和对象是什么,可以直接操作这些对象中的变量和方法,比如一个User类:
- User user=new User();
 - user.setName("Bob");
 
但是有的场景,我们无法正常去操作:
等等情况。
所以我们就需要一种机制能让我们去操作任意的类和对象。
这种机制,就是反射。简单的说,反射就是:
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。
常用API举例
先设定一个User类:
- package com.example.testapplication.reflection;
 - public class User {
 - private int age;
 - public String name;
 - public User() {
 - System.out.println("调用了User()");
 - }
 - private User(int age, String name) {
 - this.name = name;
 - this.age = age;
 - System.out.println("调用了User(age,name)"+"__age:"+age+"__name:"+name);
 - }
 - public User(String name) {
 - this.name = name;
 - System.out.println("调用了User(name)"+"__name:"+name);
 - }
 - private String getName() {
 - System.out.println("调用了getName()");
 - return this.name;
 - }
 - private String setName(String name) {
 - this.name = name;
 - System.out.println("调用了setName(name)__"+name);
 - return this.name;
 - }
 - public int getAge() {
 - System.out.println("调用了getAge()");
 - return this.age;
 - }
 - }
 
获取Class对象
实例对象的getclass方法
- //1、根据类路径获取类对象
 - try {
 - Class clz = Class.forName("com.example.testapplication.reflection.User");
 - } catch (ClassNotFoundException e) {
 - e.printStackTrace();
 - }
 - //2、直接获取
 - Class clz = User.class;
 - //3、对象的getclass方法
 - Class clz = new User().getClass();
 
获取类的构造方法
1、获取类所有构造方法
- Class clz = User.class;
 - //获取所有构造函数(不包括私有构造方法)
 - Constructor[] constructors1 = clz.getConstructors();
 - //获取所有构造函数(包括私有构造方法)
 - Constructor[] constructors2 = clz.getDeclaredConstructors();
 
2、获取类的单个构造方法
- try {
 - //获取无参构造函数
 - Constructor constructor1 = clz.getConstructor();
 - //获取参数为String的构造函数
 - Constructor constructor2 =clz.getConstructor(String.class);
 - //获取参数为int,String的构造函数
 - Class[] params = {int.class,String.class};
 - Constructor constructor3 =clz.getDeclaredConstructor(params);
 - } catch (NoSuchMethodException e) {
 - e.printStackTrace();
 - }
 
需要注意的是,User(int age, String name)为私有构造方法,所以需要使用getDeclaredConstructor获取。
调用类的构造方法生成实例对象
1、调用Class对象的newInstance方法
这个方法只能调用无参构造函数,也就是Class对象的newInstance方法不能传入参数。
- Object user = clz.newInstance();
 
2、调用Constructor对象的newInstance方法
- Class[] params = {int.class,String.class};
 - Constructor constructor3 =clz.getDeclaredConstructor(params);
 - constructor3.setAccessible(true);
 - constructor3.newInstance(22,"Bob");
 
这里要注意下,虽然getDeclaredConstructor能获取私有构造方法,但是如果要调用这个私有方法,需要设置setAccessible(true)方法,否则会报错:
- can not access a member of class com.example.testapplication.reflection.User with modifiers "private"
 
获取类的属性(包括私有属性)
- Class clz = User.class;
 - Field field1 = clz.getField("name");
 - Field field2 = clz.getDeclaredField("age");
 
同样的,getField获取public类变量,getDeclaredField可以获取所有变量(包括私有变量属性)。
所以一般直接用getDeclaredField即可。
修改实例的属性
接上例,获取类的属性后,可以去修改类实例的对应属性,比如我们有个user的实例对象,我们来修改它的name和age。
- //修改name,name为public属性
 - Class clz = User.class;
 - Field field1 = clz.getField("name");
 - field1.set(user,"xixi");
 - //修改age,age为private属性
 - Class clz = User.class;
 - Field field2 = clz.getDeclaredField("age");
 - field2.setAccessible(true);
 - field2.set(user,123);
 
获取类的方法(包括私有方法)
- //获取getName方法
 - Method method1 = clz.getDeclaredMethod("getName");
 - //获取setName方法,带参数
 - Method method2 = clz.getDeclaredMethod("setName", String.class);
 - //获取getage方法
 - Method method3 = clz.getMethod("getAge");
 
调用实例的方法
- method1.setAccessible(true);
 - Object name = method1.invoke(user);
 - method2.setAccessible(true);
 - method2.invoke(user, "xixi");
 - Object age = method3.invoke(user);
 
反射优缺点
虽然反射很好用,增加了程序的灵活性,但是也有他的缺点:
Android中的应用
插件化(Hook)
Hook 技术又叫做钩子函数,在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递。
在插件化中,我们需要找到可以hook的点,然后进行一些插件的工作,比如替换Activity,替换mH等等。这其中就用到大量反射的知识,这里以替换mH为例:
- // 获取到当前的ActivityThread对象
 - Class> activityThreadClass = Class.forName("android.app.ActivityThread");
 - Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
 - currentActivityThreadField.setAccessible(true);
 - Object currentActivityThread = currentActivityThreadField.get(null);
 - //获取这个对象的mH
 - Field mHField = activityThreadClass.getDeclaredField("mH");
 - mHField.setAccessible(true);
 - Handler mH = (Handler) mHField.get(currentActivityThread);
 - //替换mh为我们自己的HandlerCallback
 - Field mCallBackField = Handler.class.getDeclaredField("mCallback");
 - mCallBackField.setAccessible(true);
 - mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH));
 
动态代理
动态代理的特点是不需要提前创建代理对象,而是利用反射机制在运行时创建代理类,从而动态实现代理功能。
- public class InvocationTest implements InvocationHandler {
 - // 代理对象(代理接口)
 - private Object subject;
 - public InvocationTest(Object subject) {
 - this.subject = subject;
 - }
 - @Override
 - public Object invoke(Object object, Method method, Object[] args)
 - throws Throwable {
 - //代理真实对象之前
 - Object obj = method.invoke(subject, args);
 - //代理真实对象之后
 - return obj;
 - }
 - }
 
三方库(注解)
我们可以发现很多库都会用到注解,而获取注解的过程也会有反射的过程,比如获取Activity中所有变量的注解:
- public void getAnnotation(Activity activity){
 - Class clazz = activity.getClass();
 - //获得activity中的所有变量
 - Field[] fields = clazz.getDeclaredFields();
 - for (Field field : fields) {
 - field.setAccessible(true);
 - //获取变量上加的注解
 - MyAnnotation test = field.getAnnotation(MyAnnotation.class);
 - //...
 - }
 - }
 
这种通过反射处理注解的方式称作运行时注解,也就是程序运行状态的时候才会去处理注解。但是上文说过了,反射会在一定程度上影响到程序的性能,所以还有一种处理注解的方式:编译时注解。
所用到的注解处理工具是APT。
APT是一种注解处理器,可以在编译时进行扫描和处理注解,然后生成java代码文件,这种方法对比反射就能比较小的影响到程序的运行性能。
这里就不说APT的使用了,下次会专门有章节提到~
Android体系架构
Android体系架构:https://github.com/JiMuzz/Android-Architecture
参考
https://www.jianshu.com/p/3382cc765b39
https://segmentfault.com/a/1190000015860183
本文转载自微信公众号「码上积木」,可以通过以下二维码关注。转载本文请联系码上积木公众号。
                当前标题:聊聊Java知识点之反射
                
                网站链接:http://www.csdahua.cn/qtweb/news27/507127.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网