反射是如何实现的|七日打卡

反射是如何实现的|七日打卡

Android小彩虹2021-08-22 11:22:53330A+A-

反射对于开发人员来说可以说是打开潘多拉魔盒的钥匙,因为反射可以在运行时获得私有方法/私有变量,能够动态修改程序,因此我们都说反射是不安全的。

反射的定义

通常的使用方法:

  • Method method = XXX.class.getDeclaredMethod(xx,xx);
  • method.invoke(target,params);

反射的获取

须知道,想使用反射必须获取 Class 对象,而对于每个 Class 对象而言,无论创建多少个实例对象,在Java虚拟机中都只对应一个 Class 对象。

通常我们调用反射的时候,会先创建 class 对象,然后再获取它 的method 对象,接着调用 invoke 方法。
如果查阅 Method.invoke 方法,则会发现实际上委派给了 MethodAccessor 处理。MethodAccessor是一个接口,有两个具体实现:一个通过本地方法实现反射调用,一个通过委派模式。为了方便,后面都称为:本地实现/委派实现。

在第一次调用反射的时候,会调用委派实现,然后再将请求传到本地方法实现,最后再传给目标方法使用。这里有一个值得关注的点,为什么不直接调用本地实现,而是需要委派实现作为中间层呢?

原因其实也不难,主要是 Java 的反射调用机制还有一种动态生成字节码的实现(下称动态实现),也就是直接使用 invoke 指令来调用目标方法。之所以采用委派实现,便是为了能够在“本地实现”和动态实现之间来回切换。

动态实现与本地实现的区别在于,反射代码段重复运行15次以上就会使用动态实现,15次以下就使用本地实现。考虑到许多反射调用仅会执行一次,Java 虚拟机设置了一个阈值 15(可以通过 -Dsun.reflect.inflationThreshold= 来调整)

反射的缺点

反射的优点有很多,缺点也不少,这使得开发人员在使用反射时需要分外小心。 主要缺点有以下几个方面:

  1. 安全:反射使得程序运行在一个没有安全限制的环境之下。
  2. 性能开销:由于反射需要涉及类的动态解析,而且还要检查方法可见性,同时会对参数做封装和解封。
  3. 可移植性:随着JDK版本升级,涉及到反射的类库可能会影响原本的代码逻辑,即失去原本功能。

反射的优化

(1) 选择合适的api:获取反射元数据的时候避免使用遍历的方法,如:

  • 使用 Class.getField() 替代 Class.getFields()
  • 使用Class.getMethod() 替代 Class.getMethods()
  • 使用Class.getConstructor() 替代 Class.getConstructors()

简单来说,就是尽可能避免返回整个集合或者数组,除非想要获取 Class 的所有 Field、Method 或者 Constructor,这样做可以降低由于遍历或者判断带来的性能损耗。

(2) 使用缓存机制缓存反射时候操作相关元数据: 这个其实没什么好说的,因为整个反射过程中最耗时的便是获取元数据这一阶段,如 Class.forName()

(3) 使用直接操作替代反射: 这里指的是把反射操作相关元数据直接放置在类的成员变量中,降低从缓存中读取的性能开销,相当于(2)的加强版,比较典型的使用场景有JDK的动态代理。

在应用层面我们能做的优化就只有这些了,剩下的都是 Native 方法的耗时,只能通过升级 JVM(JDK) 、使用JIT编译器等非编码层面的手段提升反射的性能,同时也应该避免在高性能要求的场景下使用反射。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1
本网站由 提供CDN/云存储服务

联系我们