反射是什么

反射(Reflection)是Java程序开发语言的特征之一,它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

Oracle官方对反射的解释:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。

使用反射有如下优缺点

优点:

  1. 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性
  2. android的开发中,反射使得在版本和机型的适配兼容性上提供了大的方便

缺点:

  1. 使用反射的性能较低
  2. 使用反射相对来说不安全(另外,对于混淆类的情况很可能对应不上)
  3. 破坏了类的封装性(可以通过反射获取这个类的私有方法和属性 )

因此,反射有利有弊,需要分具体场景来择取是否真的需要反射!!!

java虚拟机实例化一个对象的流程描述:

假如你写了一段代码:Object o = new Object(); 运行了起来!
其内部运行流程为如下描述:
首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。

以上流程来自于这里的描述: https://www.zhihu.com/question/24304289

JDK反射介绍

在这里先看一下sun为我们提供了哪些操作反射的类:

java.lang.Class; //类对象
java.lang.reflect.Constructor; //构造器对象
java.lang.reflect.Field; //属性对象
java.lang.reflect.Method; //方法对象
java.lang.reflect.Modifier; //修饰符对象

注:Modifier可用来描述abstractfinalinterfacenativeprivateprotectedpublicstaticstrictfpsynchronizedtransientvolatile等。

JDK反射具体有如下用法:

注意区分带不带Declared字样的方法名区别:

  • 带有Declared时表示获取与publicprivateprotect修饰无关的所有对象,但不能是继承来的
  • 不带Declared时,表示只获取public标识符修饰的对象,且还包括从超类继承来的public对象
  1. 获取对象构造器
//获得指定参数类型params的public构造函数
Constructor getConstructor(Class[] params);//获得类的所有public构造函数
Constructor[] getConstructors(); //获得使用指定参数类型params的构造函数
Constructor getDeclaredConstructor(Class[] params);//获得类的所有的构造函数
Constructor[] getDeclaredConstructors();
  1. 获取类属性字段
//获得类中命名为name的public字段
Field getField(String name);//获得类的所有public字段
Field[] getFields();//获得类中命名为name的字段
Field getDeclaredField(String name);//获得类中的所有的字段
Field[] getDeclaredFields();
  1. 获取类方法
//使用指定参数类型params,获得名称为name的public方法
Method getMethod(String name, Class[] params);//获得类中所有的public方法
Method[] getMethods();//使用指定参数类型params,获得命名为name的方法
Method getDeclaredMethod(String name, Class[] params);//获取类中的所有方法
Method[] getDeclaredMethods();

android开发中常见用法如下(其他用法不再进行测试描述,具体可以参见GH-Demo):

public static int getStatusBarHeight(Context context) {try {//构造dimen类对象Class<?> c = Class.forName("com.android.internal.R$dimen");Object obj = c.newInstance(); //使用无参构造函数实例化dimen对象Field field = c.getField("status_bar_height"); //获取obj对象中名称为status_bar_height的Field字段field.setAccessible(true); //修改访问修饰属性(假设为private修饰)int height = Integer.parseInt(field.get(obj).toString()); //从obj对象中读取field字段对应的valuereturn context.getResources().getDimensionPixelSize(height);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return 0;
}

下面介绍一下网上比较热门且自认为用法比较灵活的java反射封装类库:jOOR

jOOR官方介绍

jOOR - Fluent Reflection in Java jOOR is a very simple fluent API that gives access to your Java Class structures in a more intuitive way. The JDK’s reflection APIs are hard and verbose to use. Other languages have much simpler constructs to access type meta information at runtime. Let us make Java reflection better. http://www.jooq.org/products

大意为:jOOR提供了一种更为直观的方式来构建JDK原生的反射调用,因为JDK提供的反射API使用起来较冗长(它对包java.lang.reflect进行了简单封装,使得反射更加方便)。

gradle配置为:compile'org.jooq:joor:0.9.6'

先看一个简单例子(同上述方法getStatusBarHeight()),对比JDK的反射API和jOOR的方法调用。

public static int getStatusBarHeight(Context context) {final int statusHeightId = Reflect.on("com.android.internal.R$dimen").create().field("status_bar_height").get();return context.getResources().getDimensionPixelSize(statusHeightId);
}

从上面的例子就可以看到jOOR明显的优势:

  1. 简化了反射冗长的异常处理。
  2. 简化了对ClassMethod等反射类的实例化,改为统一Reflect替换。
  3. 支持private方法的调用,内部动态区分是否需要accessible()
  4. 将反射调用封装为更流行的链式调用方式,代码更容易理解(类似Rxjava的封装方式)。

jOOR功能介绍

  1. 提供on()操作符对Class、对象(还包括方法、构造函数)进行统一实例化为Reflect对象,后续所有的反射操作基于该Reflect对象进行。
  2. 调用方式均被封装成返回Reflect对象的链式结构,在使用上使得代码更加简洁。
  3. 对方法的签名匹配封装了更完善的匹配规则,包括精确匹配exactMethod()、近似匹配similarMethod()【对函数参数的近似匹配(int -> Integer)】和基类搜索等。
  4. 调用私有方法的不需要显示调用setAccessible(),内部动态读取修饰标记自动适配。
  5. 更加简洁的实现了对象构造函数的反射调用create()方法。
  6. 函数的调用call()方法组合成了可以拼接在Reflect的对象后面的链式方法。
  7. 额外增加了高级操作符as(),它实现了类的代理访问以及POJO对象get/set/is方法实现。

jOOR API介绍

  1. 通过类名转换成一个Reflect对象
public static Reflect on(String name);
public static Reflect on(String name, ClassLoader classLoader);
public static Reflect on(Class<?> clazz);
  1. 将一个对象转换成一个Reflect对象
public static Reflect on(Object object);
  1. 修改一个AccessibleObject类型的对象的访问权限
public static <T extends AccessibleObject> T accessible(T accessible);
  1. 返回Reflect对象具体包装的类型,类型为Class或者对象,具体由操作符on()的重载参数决定
public <T> T get();
  1. name指定的field转换成一个Reflect对象,后续对field的操作变为对Reflect对象的操作
public Reflect field(String name);
  1. 返回当前Reflect对象的所有field属性,并转换成Reflect对象的map
public Map<String, Reflect> fields();
  1. 修改(获取)field属性值
public Reflect set(String name, Object value);
public <T> T get(String name);
  1. 反射调用name指定的函数,此函数封装了对函数的签名的精准匹配和近似匹配
public Reflect call(String name);
public Reflect call(String name, Object... args);
  1. 反射调用指定类的构造函数,封装了精准匹配和近似匹配
public Reflect create();
public Reflect create(Object... args);
  1. 返回当前Reflect对象封装的对象类型
public Class<?> type();
  1. 给封装对象创建一个代理访问,还实现了对POJO对象setXX/getXX/isxx功能(此为Reflect对象的高级功能)
public <P> P as(Class<P> proxyType);

jOOR 典型用法

先列出测试举例TestField类定义以便有个大概的概念:
声明实例:TestField testField = new TestField();

public static class TestField {/*** private*/private int INT1 = new Integer(0);private Integer INT2 = new Integer(2);/*** private & static*/private static int S_INT1 = new Integer(3);private static Integer S_INT2 = new Integer(0);/*** private & final*/private final int F_INT3 = new Integer(4);/*** object*/private TestField I_DATA;public void tetProxy(String message) {Log.d("TestJOOR", "tetProxy: " + message);}
}

代理接口POJOInterface的定义:

public interface POJOInterface {String substring(int beginIndex, int endIndex);void tetProxy(String message);void setFoo(String foo);String getFoo();void setBaz(String baz);String getBaz();
}

POJO对象的Map实现:
构造实例 Map<String, Object> map = new TestBean();

private class TestBean extends HashMap<String, Object> {private String baz;public void setBaz(String baz) {this.baz = "POJO-MyMap: " + baz;}public String getBaz() {return baz;}
}
  1. 调用String的非静态方法(通过实例对象构造Reflect
String value = "1234";
String subString  = Reflect.on((Object) value).call("substring", 0, 2).get();
//结果为:subString = "123";
  1. 调用String的静态方法(通过String.class类名构造Reflect
String valueOf = Reflect.on(String.class).call("valueOf", true).get();
//结果为:valueOf = "true";
  1. 修改private属性
int init_int = Reflect.on(testField).get("INT2"); //init_int=2;
Reflect setReflect = Reflect.on(testField).set("INT2", 300);
int result = setReflect.field("INT2").get(); //result = 300;
  1. 修改static属性
int sInit_int = Reflect.on(TestField.class).get("S_INT1"); //sInit_int = 3;
Reflect obj = Reflect.on("com.android.test.joor.TestJOOR$TestField").set("S_INT2", 500);
int sInt2 = obj.field("S_INT2").get(); //sInt2 = 500;
  1. 复杂链式修改多个属性值
Reflect.on(testField).set("I_DATA", Reflect.on(TestField.class).create()).field("I_DATA").set("INT1", 700).set("INT2", 800);
  1. interface实现类对象的代理
String asResult = Reflect.on((Object) "abc").as(POJOInterface.class).substring(1, 2);
//测试结果: asResult = "bc";Reflect.on(testField).as(POJOInterface.class).tetProxy("this is proxy test!!");
//测试结果:调用TestField类中的tetProxy()函数。
  1. 对于没有实现set/get方法的Bean对象,对应的数据有以key-valuekeysetXx()xx后缀)的形式存放在HashMap
Reflect.on(map).as(POJOInterface.class).setFoo("abc");
int size = map.size(); //size = 1;
String value1 = (String) map.get("foo"); //value1 = "abc";
String value2 = Reflect.on(map).as(POJOInterface.class).getFoo(); //value2 = "abc";
  1. 已经实现了set/get方法的Bean对象,对应数据存放在Bean对象的字段中
Reflect.on(map).as(POJOInterface.class).setBaz("baz");
int size = map.size(); //size = 0; 没有存放在hasMap中
String value4 = (String) map.get("baz"); //value4 = null;
String value5 = Reflect.on(map).as(POJOInterface.class).getBaz(); //value5 = "MyMap: baz-test";

备注:
jOOR对反射的原始异常进行了转义---ReflectException,其直接基类为RuntimeException,这里意味着通过jOOR库的api开发时,不会强制开发人员用try...catch括起来,但一旦发生异常,程序就会中断运行

反射调用效率优化

从应用层面看,想对反射的执行效率做提升优化,只能在项目中明令禁止使用反射了,如果不可规避(一般避免不了),只能最大限度的降低反射的调用次数了。一般的做法是将第一次获取的ClassMethodField对象进行缓存起来,下次调用同样的反射对象时直接取已缓存对象进行相应调用。这里引用另一个反射调用的封装库 reflect,其内部就是将ClassMethodField等对象进行缓存以备下次调用,但个人认为其用法不如jOOR灵活,但优化思路值得参考。
本人参考reflect库的优化思路对jOOR内部进行修改,在不破坏外部调用接口的前提下对内部的ClassConstructorMethodField进行缓存。地址为GH-Demo-Reflect.java

反射相关知识及jOOR反射库介绍相关推荐

  1. 安卓逆向-new-sec6-4 Java反射相关知识以及平头哥框架hook构造函数 | App发布测试版本感染

    反射机制 app加上这个属性,也能发布测试版本,被成功感染,无需签名和发布那个release版本 APP是E:\1A_androidstudio_project\course4 插件是E:\1A_an ...

  2. 你该知道的深度强化学习相关知识

    如今,机器学习(Machine Learning,ML)和人工智能(Artificial Intelligence,AI)的相关算法越来越深度地融合到了我们的社会与生活中,并且在金融科技.医疗保健.以 ...

  3. Dash相关知识总结

    Dash相关知识总结 1. DASH介绍 DASH,又叫MPEG DASH,DASH:Dynamic Adaptive Streaming over HTTP ,是一种在互联网上传送动态码率的Vide ...

  4. 硬件知识:固态硬盘相关知识介绍

    今天就为大家全面科普一下固态硬盘的相关知识,让大家购买时做到心中有数,按需选择. 首先还是从SSD的结构来说起,SSD最基本的组成部件分为:主控芯片.闪存芯片.固件算法,下面我们分别阐述三者的工作职责 ...

  5. 后端技术:消息队列MQ/JMS/Kafka相关知识介绍

    ?今天给大家分享消息队列MQ/JMS/Kafka相关知识介绍 1.消息队列介绍 首先举个收快递的栗子,传统的收快递,快递小哥把我们的快递送到我们的手里.他需要什么条件嗯? 快递小哥有时间送, 我们有时 ...

  6. [Redis6]Redis相关知识介绍

    Redis介绍相关知识 端口6379 6379 是 "MERZ " 九宫格输入法对应的数字.Alessia Merz 是一位意大利舞女.女演员. Redis 作者 Antirez ...

  7. pdh光端机相关知识介绍

    目前随着网络信息技术的发展,我们拥有了更加先进的技术运用技巧,其中一个就是pdh光端机的使用.我们自从使用了pdh光端机,对于信息的传输大大的打破了传统的信息传输的缺点.那么,作为一种新生的技术,相信 ...

  8. java窗口三栏布局_移动端的flex三栏布局的相关知识介绍(代码示例)

    本篇文章给大家带来的内容是关于移动端的flex三栏布局的相关知识介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 默认情况下先显示移动端,通过 @media 属性适配屏 ...

  9. 弧形背景html,弧形背景墙—弧形背景墙相关知识介绍

    现在的装修方式真的是很多的,背景墙就是现在一种新型的装修方式,而且就算是一个小小的背景墙,它也是有很多形状的,比如弧形.圆形等.今天小编要给大家介绍的是弧形背景墙的相关知识. 弧形背景墙 弧形背景墙- ...

  10. 工业视觉系统相关知识和选型介绍(一):相机篇

    工业视觉系统相关知识和选型介绍(一):相机篇 一.工业视觉系统 二.工业相机 三.关键名词解释 四.工业相机选型 五.工业相机品牌 六.相机选型实例 一.工业视觉系统 1.机器视觉就是用机器代替人眼来 ...

最新文章

  1. linux通信中recv,linux套接字通信之recv中的缓存机制的研究
  2. java cloneable 用途_java中cloneable的使用
  3. 根据class名 赋值_匿名内部类 类名规则
  4. 《TCP/IP Socket in C》阅读笔记
  5. C语言多维数组本质技术推演
  6. 水环境模型与大数据技术融合研究
  7. JS 基础知识点及常考面试题(二)
  8. php生成option,php递归实现无限分类生成下拉列表的函数
  9. Bootstrap-基于jquery的bootstrap在线文本编辑器插件Summernote
  10. SAP License:如何用Coding Block
  11. 数据线为什么不弄两头都是Typec接口的呢?
  12. [译] 使用 iPhone X 与 Maya 实现快速面部捕捉
  13. 用python模拟微信支付_微信app支付python代码实现
  14. 学会Java输入输出流,看这一篇就够了,建议收藏!
  15. aspnet mvc 中 跨域请求的处理方法
  16. 南海云课堂春季10(T)K3
  17. Android 查看内存使用工具 (procstats)
  18. 获取淘宝商品历史价格信息API(PHP,JAVA都可对接)
  19. 全新iPhone发布会确定时间了!从Loog上可以看出有新配色
  20. 4.9-11 ebook 小问题,自摘记,书城首页开发完毕。

热门文章

  1. 浅谈文件断点续传和WebUploader的基本结合
  2. 在网页上获取当前日期,数字时钟
  3. iPads和iPhones的Media Queries(转载)
  4. 线索二叉树实现(中序)
  5. Flex 学习笔记 提高编译速度
  6. HDU2066--一个人的旅行(Dijkstra)
  7. 时间操作(JavaScript版)—页面显示格式:年月日星期几
  8. 教你一秒理解setInterval与setTimeout的使用和区别
  9. Windows下Cesium Terrain Builder编译 (VS2015)
  10. 解决使用PowerShell执行命令出现“因为在此系统上禁止运行脚本”的问题