反射(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.
简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
优点:
缺点:
因此,反射有利有弊,需要分具体场景来择取是否真的需要反射!!!
假如你写了一段代码:Object o = new Object();
运行了起来!
其内部运行流程为如下描述:
首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。
以上流程来自于这里的描述: https://www.zhihu.com/question/24304289
在这里先看一下sun为我们提供了哪些操作反射的类:
java.lang.Class; //类对象
java.lang.reflect.Constructor; //构造器对象
java.lang.reflect.Field; //属性对象
java.lang.reflect.Method; //方法对象
java.lang.reflect.Modifier; //修饰符对象
注:Modifier
可用来描述abstract
、final
、interface
、native
、private
、protected
、public
、static
、strictfp
、synchronized
、transient
、volatile
等。
JDK反射具体有如下用法:
注意区分带不带Declared
字样的方法名区别:
Declared时
表示获取与public
、private
、protect
修饰无关的所有对象,但不能是继承来的;Declared
时,表示只获取public
标识符修饰的对象,且还包括从超类继承来的public
对象。
//获得指定参数类型params的public构造函数
Constructor getConstructor(Class[] params);
//获得类的所有public构造函数
Constructor[] getConstructors();
//获得使用指定参数类型params的构造函数
Constructor getDeclaredConstructor(Class[] params);
//获得类的所有的构造函数
Constructor[] getDeclaredConstructors();
//获得类中命名为name的public字段
Field getField(String name);
//获得类的所有public字段
Field[] getFields();
//获得类中命名为name的字段
Field getDeclaredField(String name);
//获得类中的所有的字段
Field[] getDeclaredFields();
//使用指定参数类型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字段对应的value
return 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 - 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
明显的优势:
Class
、Method
等反射类的实例化,改为统一Reflect
替换。private
方法的调用,内部动态区分是否需要accessible()
。Rxjava
的封装方式)。on()
操作符对Class
、对象(还包括方法、构造函数)进行统一实例化为Reflect
对象,后续所有的反射操作基于该Reflect
对象进行。Reflect
对象的链式结构,在使用上使得代码更加简洁。exactMethod()
、近似匹配similarMethod()
【对函数参数的近似匹配(int -> Integer
)】和基类搜索等。setAccessible()
,内部动态读取修饰标记自动适配。create()
方法。call()
方法组合成了可以拼接在Reflect
的对象后面的链式方法。as()
,它实现了类的代理访问以及POJO对象
的get/set/is
方法实现。Reflect
对象
public static Reflect on(String name);
public static Reflect on(String name, ClassLoader classLoader);
public static Reflect on(Class<?> clazz);
Reflect
对象
public static Reflect on(Object object);
AccessibleObject
类型的对象的访问权限
public static <T extends AccessibleObject> T accessible(T accessible);
Reflect
对象具体包装的类型,类型为Class
或者对象,具体由操作符on()
的重载参数决定
public <T> T get();
name
指定的field
转换成一个Reflect
对象,后续对field
的操作变为对Reflect
对象的操作
public Reflect field(String name);
Reflect
对象的所有field
属性,并转换成Reflect
对象的map
public Map<String, Reflect> fields();
field
属性值
public Reflect set(String name, Object value);
public <T> T get(String name);
name
指定的函数,此函数封装了对函数的签名的精准匹配和近似匹配
public Reflect call(String name);
public Reflect call(String name, Object... args);
public Reflect create();
public Reflect create(Object... args);
Reflect
对象封装的对象类型
public Class<?> type();
POJO对象
的setXX/getXX/isxx
功能(此为Reflect
对象的高级功能)
public <P> P as(Class<P> proxyType);
先列出测试举例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;
}
}
String
的非静态方法(通过实例对象构造Reflect
)
String value = "1234";
String subString = Reflect.on((Object) value).call("substring", 0, 2).get();
//结果为:subString = "123";
String
的静态方法(通过String.class
类名构造Reflect
)
String valueOf = Reflect.on(String.class).call("valueOf", true).get();
//结果为:valueOf = "true";
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;
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;
Reflect.on(testField).set("I_DATA", Reflect.on(TestField.class).create())
.field("I_DATA").set("INT1", 700).set("INT2", 800);
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()函数。
set/get
方法的Bean对象,对应的数据有以key-value
(key
为setXx()
的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";
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
括起来,但一旦发生异常,程序就会中断运行。
从应用层面看,想对反射的执行效率做提升优化,只能在项目中明令禁止使用反射了,如果不可规避(一般避免不了),只能最大限度的降低反射的调用次数了。一般的做法是将第一次获取的Class
、Method
、Field
对象进行缓存起来,下次调用同样的反射对象时直接取已缓存对象进行相应调用。这里引用另一个反射调用的封装库 reflect,其内部就是将Class
、Method
、Field
等对象进行缓存以备下次调用,但个人认为其用法不如jOOR
灵活,但优化思路值得参考。
本人参考reflect库的优化思路对jOOR
内部进行修改,在不破坏外部调用接口的前提下对内部的Class
、Constructor
、Method
、Field
进行缓存。地址为GH-Demo-Reflect.java