文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

聊聊Java知识点之反射

2024-12-03 12:42

关注

前言

今天说Java模块内容:反射。

反射介绍

正常情况下,我们知晓我们要操作的类和对象是什么,可以直接操作这些对象中的变量和方法,比如一个User类:

  1. User user=new User(); 
  2. user.setName("Bob"); 

但是有的场景,我们无法正常去操作:

等等情况。

所以我们就需要一种机制能让我们去操作任意的类和对象。

这种机制,就是反射。简单的说,反射就是:

对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

常用API举例

先设定一个User类:

  1. package com.example.testapplication.reflection; 
  2. public class User { 
  3.     private int age; 
  4.     public String name
  5.  
  6.     public User() { 
  7.         System.out.println("调用了User()"); 
  8.     } 
  9.  
  10.     private User(int age, String name) { 
  11.         this.name = name
  12.         this.age = age; 
  13.         System.out.println("调用了User(age,name)"+"__age:"+age+"__name:"+name); 
  14.     } 
  15.  
  16.     public User(String name) { 
  17.         this.name = name
  18.         System.out.println("调用了User(name)"+"__name:"+name); 
  19.     } 
  20.  
  21.     private String getName() { 
  22.         System.out.println("调用了getName()"); 
  23.         return this.name
  24.     } 
  25.  
  26.     private String setName(String name) { 
  27.      this.name = name
  28.         System.out.println("调用了setName(name)__"+name); 
  29.         return this.name
  30.     } 
  31.  
  32.     public int getAge() { 
  33.         System.out.println("调用了getAge()"); 
  34.         return this.age; 
  35.     }     

获取Class对象

实例对象的getclass方法

  1. //1、根据类路径获取类对象 
  2. try { 
  3.     Class clz = Class.forName("com.example.testapplication.reflection.User"); 
  4. } catch (ClassNotFoundException e) { 
  5.     e.printStackTrace(); 
  6.  
  7. //2、直接获取 
  8. Class clz = User.class; 
  9.  
  10. //3、对象的getclass方法 
  11. Class clz = new User().getClass(); 

获取类的构造方法

获取类所有构造方法

  1. Class clz = User.class; 
  2. //获取所有构造函数(不包括私有构造方法) 
  3. Constructor[] constructors1 = clz.getConstructors(); 
  4. //获取所有构造函数(包括私有构造方法) 
  5. Constructor[] constructors2 = clz.getDeclaredConstructors(); 

获取类的单个构造方法

  1. try { 
  2.        //获取无参构造函数 
  3.        Constructor constructor1 = clz.getConstructor(); 
  4.  
  5.        //获取参数为String的构造函数 
  6.        Constructor constructor2 =clz.getConstructor(String.class); 
  7.  
  8.        //获取参数为int,String的构造函数 
  9.        Class[] params = {int.class,String.class}; 
  10.        Constructor constructor3 =clz.getDeclaredConstructor(params); 
  11.    } catch (NoSuchMethodException e) { 
  12.        e.printStackTrace(); 
  13.    } 

需要注意的是,User(int age, String name)为私有构造方法,所以需要使用getDeclaredConstructor获取。

调用类的构造方法生成实例对象

调用Class对象的newInstance方法

这个方法只能调用无参构造函数,也就是Class对象的newInstance方法不能传入参数。

  1. Object user = clz.newInstance(); 

调用Constructor对象的newInstance方法

  1. Class[] params = {int.class,String.class}; 
  2. Constructor constructor3 =clz.getDeclaredConstructor(params); 
  3. constructor3.setAccessible(true); 
  4. constructor3.newInstance(22,"Bob"); 

这里要注意下,虽然getDeclaredConstructor能获取私有构造方法,但是如果要调用这个私有方法,需要设置setAccessible(true)方法,否则会报错:

  1. can not access a member of class com.example.testapplication.reflection.User with modifiers "private" 

获取类的属性(包括私有属性)

  1. Class clz = User.class; 
  2. Field field1 = clz.getField("name"); 
  3. Field field2 = clz.getDeclaredField("age"); 

同样的,getField获取public类变量,getDeclaredField可以获取所有变量(包括私有变量属性)。

所以一般直接用getDeclaredField即可。

修改实例的属性

接上例,获取类的属性后,可以去修改类实例的对应属性,比如我们有个user的实例对象,我们来修改它的name和age。

  1. //修改namenamepublic属性 
  2. Class clz = User.class; 
  3. Field field1 = clz.getField("name"); 
  4. field1.set(user,"xixi"); 
  5.  
  6. //修改age,age为private属性 
  7. Class clz = User.class; 
  8. Field field2 = clz.getDeclaredField("age"); 
  9. field2.setAccessible(true); 
  10. field2.set(user,123); 

获取类的方法(包括私有方法)

  1.   //获取getName方法 
  2.    Method method1 = clz.getDeclaredMethod("getName"); 
  3. //获取setName方法,带参数 
  4.    Method method2 = clz.getDeclaredMethod("setName", String.class); 
  5.    //获取getage方法 
  6.    Method method3 = clz.getMethod("getAge"); 

调用实例的方法

  1. method1.setAccessible(true); 
  2. Object name = method1.invoke(user); 
  3.  
  4.  
  5. method2.setAccessible(true); 
  6. method2.invoke(user"xixi"); 
  7.  
  8. Object age = method3.invoke(user); 

反射优缺点

虽然反射很好用,增加了程序的灵活性,但是也有他的缺点:

Android中的应用

插件化(Hook)

Hook 技术又叫做钩子函数,在系统没有调用该函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递。

在插件化中,我们需要找到可以hook的点,然后进行一些插件的工作,比如替换Activity,替换mH等等。这其中就用到大量反射的知识,这里以替换mH为例:

  1. // 获取到当前的ActivityThread对象 
  2. Class activityThreadClass = Class.forName("android.app.ActivityThread"); 
  3. Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); 
  4. currentActivityThreadField.setAccessible(true); 
  5. Object currentActivityThread = currentActivityThreadField.get(null); 
  6.  
  7. //获取这个对象的mH 
  8. Field mHField = activityThreadClass.getDeclaredField("mH"); 
  9. mHField.setAccessible(true); 
  10. Handler mH = (Handler) mHField.get(currentActivityThread); 
  11.  
  12.  
  13. //替换mh为我们自己的HandlerCallback 
  14. Field mCallBackField = Handler.class.getDeclaredField("mCallback"); 
  15. mCallBackField.setAccessible(true); 
  16. mCallBackField.set(mH, new MyActivityThreadHandlerCallback(mH)); 

动态代理

动态代理的特点是不需要提前创建代理对象,而是利用反射机制在运行时创建代理类,从而动态实现代理功能。

  1. public class InvocationTest implements InvocationHandler { 
  2.     // 代理对象(代理接口) 
  3.     private Object subject; 
  4.  
  5.     public InvocationTest(Object subject) { 
  6.         this.subject = subject; 
  7.     } 
  8.     @Override 
  9.     public Object invoke(Object object, Method method, Object[] args) 
  10.             throws Throwable { 
  11.         //代理真实对象之前 
  12.         Object obj = method.invoke(subject, args); 
  13.         //代理真实对象之后 
  14.         return obj; 
  15.     } 

三方库(注解)

我们可以发现很多库都会用到注解,而获取注解的过程也会有反射的过程,比如获取Activity中所有变量的注解:

  1. public void getAnnotation(Activity activity){ 
  2.     Class clazz = activity.getClass(); 
  3.     //获得activity中的所有变量 
  4.     Field[] fields = clazz.getDeclaredFields(); 
  5.     for (Field field : fields) { 
  6.         field.setAccessible(true); 
  7.         //获取变量上加的注解 
  8.         MyAnnotation test = field.getAnnotation(MyAnnotation.class); 
  9.         //... 
  10.     } 

这种通过反射处理注解的方式称作运行时注解,也就是程序运行状态的时候才会去处理注解。但是上文说过了,反射会在一定程度上影响到程序的性能,所以还有一种处理注解的方式:编译时注解。

所用到的注解处理工具是APT。

APT是一种注解处理器,可以在编译时进行扫描和处理注解,然后生成java代码文件,这种方法对比反射就能比较小的影响到程序的运行性能。

这里就不说APT的使用了,下次会专门有章节提到~

Android体系架构

Android体系架构:https://github.com/JiMuzz/Android-Architecture

参考

https://www.jianshu.com/p/3382cc765b39

https://segmentfault.com/a/1190000015860183

本文转载自微信公众号「码上积木」,可以通过以下二维码关注。转载本文请联系码上积木公众号。

 

来源:码上积木内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯