Java/Android反射
1.简介
反射是Java强大的技术,可以反射一个类、反射方法(构造函数)、反射字段。一个比较好的开源实现是jOOR,但是不适用于Android定义为final的字段。
在反射语法中最难的莫过于反射一个泛型类。
反射包括一下技术:
- 根据一个字符串得到一个类的对象。
- 获取一个类的所有公有或者私有、静态或实例的字段、方法、属性。
- 对泛型类的反射。
2. 基本反射语法
2.1 根据一个字符串得到一个类
- getClass
String str = “abc”;
Class cl = str.getClass();
- Class.forName
通过字符串获取一个类型
Class c2 = Class.forName("java.lang.String");
Class c3 = Class.forName("android.widget.Button");
//通过getSuperClass,每个Class都有这个函数
Class c5 = c3.getSuperclass(); //得到TextView
- class属性
每个类都有class属性,可以得到这个类的类型:
Class c6 = String.class;
Class c7 = java.lang.String.class;
Class c8 = MainActivity.InnerClass.class;
Class c9 = int.class;
Class c10 = int[].class;
- TYPE属性
基本类型封装类,如BOOLEAN,都有TYPE属性,可以得到这个基本类型的类型:
Class c11 = Boolean.TYPE;
Class c12 = Byte.TYPE;
Class c13 = Character.TYPE;
Class c14 = Short.TYPE;
Class c15 = Integer.TYPE;
Class c16 = Long.TYPE;
Class c17 = Float.TYPE;
Class c18 = Double.TYPE;
Class c19 = Void.TYPE;
2.2 获取类构造函数
构造函数包括private
和public
两种,也支持无参数和有参数两种类型,下面的TestClassCtor类就有很多构造函数:
public class TestClassCtor {
private String name;
private static String address = "abc";
public TestClassCtor() {
name = "test";
}
public TestClassCtor(int a) {
}
public TestClassCtor(int a, String b) {
name = b;
}
private TestClassCtor(int a, double c) {
}
private String doSOmething(String d) {
Log.v("test", "TestClassCtor, doSOmething");
return "abcd";
}
private static void work() {
Log.v("test", "TestClassCtor, work");
}
public String getName() {
return name;
}
public static void printAddress() {
Log.v("test", address);
}
}
1)获取类的所有构造函数。
通过Class的getDeclaredConstructors
方法,获取类的所有构造函数,包括public和private的构造函数,然后通过for循环打印
TestClassCtor r = new TestClassCtor();
Class temp = r.getClass();
String className = temp.getName(); // 获取指定类的类名
Log.v("test", "获取类的所有ctor,不分public还是private----------------------------------------------");
//获取类的所有ctor,不分public还是private
try {
Constructor[] theConstructors = temp.getDeclaredConstructors(); // 获取指定类的公有构造方法
for (int i = 0; i < theConstructors.length; i++) {
int mod = theConstructors[i].getModifiers(); // 输出修饰域和方法名称
Log.v("test", Modifier.toString(mod) + " " + className + "(");
Class[] parameterTypes = theConstructors[i].getParameterTypes(); // 获取指定构造方法的参数的集合
for (int j = 0; j < parameterTypes.length; j++) { // 输出打印参数列表
Log.v("test", parameterTypes[j].getName());
if (parameterTypes.length > j + 1) {
Log.v("test", ", ");
}
}
Log.v("test", ")");
}
} catch (Exception e) {
e.printStackTrace();
}
这里注意的是,获取public的构造函数可以使用getConstructors,而其他修饰符的构造函数只能使用getDeclaredConstructors
2) 获取指定的构造函数。
获取无参数构造函数:
Constructor c1 = temp.getDeclaredConstructor();
获取有参数构造函数:
Class[] p2 = {int.class};
Constructor c2 = temp.getDeclaredConstructor(p2);
反射到类的构造函数很重要,通过执行该类的构造函数可以得到类的实例。
3)调用构造函数
通过反射调用类的构造函数得到类的实例,这要借助于Constructor
的newInstance
方法:
Class r = Class.forName("com.calvin.testreflection.TestClassCtor");
//含参
Class[] p3 = {int.class, String.class};
Constructor ctor = r.getDeclaredConstructor(p3);
Object obj = ctor.newInstance(1, "bjq");
//无参
Constructor ctor2 = r.getDeclaredConstructor();
Object obj2 = ctor2.newInstance();
//也可以使用Class的newInstance方法,但Class仅提供默认无参的实例化方法
Object obj4 = r.newInstance();
2.3 获取类的私有实例方法并调用它
在TestClassCtor
中,获取并调用私有方法doSomething()
的代码如下:
//以下4句话,创建一个对象
Class r = Class.forName("com.calvin.testreflection.TestClassCtor");
Class[] p3 = {int.class, String.class};
Constructor ctor = r.getDeclaredConstructor(p3);
Object obj = ctor.newInstance(1, "bjq");
//以下4句话,调用一个private方法
Class[] p4 = {String.class};
Method method = r.getDeclaredMethod("doSomething", p4); //在指定类中获取指定的方法
method.setAccessible(true);
Object argList[] = {"calvin"}; //这里写死,下面有个通用的函数getMethodParamObject
Object result = method.invoke(obj, argList);
2.4 获取类的静态私有方法并调用它
在TestClassCtor
中,获取并调用静态私有方法work()
的代码如下:
//以下4句话,创建一个对象
Class r = Class.forName("com.calvin.testreflection.TestClassCtor");
//以下3句话,调用一个private静态方法
Method method = r.getDeclaredMethod("work"); //在指定类中获取指定的方法
method.setAccessible(true);
method.invoke(null);
2.5 获取类的私有实例字段并修改它
在TestClassCtor
中,获取并修改私有实例字段name
的代码如下:
//以下4句话,创建一个对象
Class r = Class.forName("com.calvin.testreflection.TestClassCtor");
Class[] p3 = {int.class, String.class};
Constructor ctor = r.getDeclaredConstructor(p3);
Object obj = ctor.newInstance(1, "bjq");
//获取name字段,private
Field field = r.getDeclaredField("name");
field.setAccessible(true);
Object fieldObject = field.get(obj);
//只对obj有效
field.set(obj, "calvin88");
TestClassCtor testClassCtor = new TestClassCtor(100);
testClassCtor.getName(); //仍然返回null,并没有修改
2.6 获取类的私有静态字段并修改它
在TestClassCtor
中,获取并修改静态私有实例字段address
的代码如下:
//以下4句话,创建一个对象
Class r = Class.forName("com.calvin.testreflection.TestClassCtor");
//获取address静态字段,private
Field field = r.getDeclaredField("address");
field.setAccessible(true);
Object fieldObject = field.get(null);
field.set(fieldObject, "ABCD");
//静态变量,一次修改,终生受用
TestClassCtor.printAddress();
静态字段的值被修改后,下次再使用,字段的值是修改后的值。
2.7 泛型类反射
Android源码中大量使用泛型,插件化技术中,离不开对泛型的反射,比如通过泛型单例(Singleton)创建对象,下面代码演示如何反射泛型单例中的真实类型:
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
通过抽象单例类创建单例代码如下,其中ClassB2Interface
是一个简单的接口,ClassB2
是该简单接口的具体实现
public class AMN {
private static final Singleton<ClassB2Interface> gDefault = new Singleton<ClassB2Interface>() {
protected ClassB2Interface create() {
ClassB2 b2 = new ClassB2();
b2.id = 2;
return b2;
}
};
static public ClassB2Interface getDefault() {
return gDefault.get();
}
}
通过反射获取Singleton
类中的mInstance
字段的代码如下:
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段
Class<?> singleton = Class.forName("com.calvin.testreflection.Singleton");
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//获取AMN的gDefault单例gDefault,gDefault是静态的
Class<?> activityManagerNativeClass = Class.forName("com.calvin.testreflection.AMN");
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null);
// AMN的gDefault对象里面原始的 B2对象
Object rawB2Object = mInstanceField.get(gDefault);
对接口类型实例的反射可以应用于动态代理,通过动态代理技术可以完成很多有用的事情,比如著名的Retrofit框架、方法拦截处理,打桩等
对ClassB2Interface动态代理实现代码如下:
// 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> classB2Interface = Class.forName("com.calvin.testreflection.ClassB2Interface");
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { classB2Interface },
new ClassB2Mock(rawB2Object));
mInstanceField.set(gDefault, proxy);
其中ClassB2Mock
代码如下:
class ClassB2Mock implements InvocationHandler {
Object mBase;
public ClassB2Mock(Object base) {
mBase = base;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.e("calvin", method.getName());
if ("doSomething".equals(method.getName())) {
print();
}
return method.invoke(mBase, objects);
}
void print() {
Log.v("baobao", "hello");
}
}
3. jOOR
上一节中的反射实现都是基于Java原生语法,推荐一个简单的Java反射实现库jOOR,代码量比较少,只有几个java文件,github地址如下:
https://github.com/jOOQ/jOOR
提供了java6,java8,java9的兼容实现,可以根据你的环境选择合适的版本。
jOOR中用于反射的类主要有两个Reflect.java和ReflectException.java,其中Reflect.java最为重要,包括6个核心方法:
- on:包裹一个类或者对象,表示在这个类或者对象上进行反射,类的值可以是Class,也可以是完整的类名
- create:用来调用之前的类的构造方法,有两种重载,一种有参数,一种无参数。
- call:方法调用,传入方法名和参数,如有返回值还需要调用get
- get:获取(field和method返回)值相关,会进行类型转换,常与call组合使用。
- set:设置属性。
还是以TestClassCtor
类为例子简单举例说明jOOR的使用
3.1 获取并调用类的构造函数
TestClassCtor r = new TestClassCtor();
Class temp = r.getClass();
String className = temp.getName(); // 获取指定类的类名
//public构造函数
Object obj = on(temp).create().get();
Object obj2 = on(temp).create(1, "abc").get();
//private构造函数
TestClassCtor obj3 = on(TestClassCtor.class).create(1, 1.1).get();
String a = obj3.getName();
3.2 获取类的私有实例方法
TestClassCtor r = new TestClassCtor();
Class temp = r.getClass();
Reflect reflect = on(temp).create();
//调用一个private实例方法
String a1 = reflect.call("doSOmething", "param1").get();
//调用一个public实例方法
String a2 = reflect.call("getName").get();
3.3 获取类的私有静态方法
//调用一个private静态方法
on(TestClassCtor.class).call("work").get();
//调用一个public静态方法
on(TestClassCtor.class).call("printAddress").get();
3.4 获取类的私有实例字段
获取类的私有实例字段并修改它:
Reflect obj = on("com.calvin.testreflection.TestClassCtor")
.create(1, 1.1);
obj.set("name", "calvin");
Object obj1 = obj.get("name");
3.5 获取类的私有静态字段
on("com.calvin.testreflection.TestClassCtor")
.set("address", "avcccc");
Object obj2 = on("com.calvin.testreflection.TestClassCtor")
.get("address");
3.6 泛型反射
//获取AMN的gDefault单例gDefault,gDefault是静态的
Object gDefault = on("com.calvin.testreflection.AMN").get("gDefault");
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的mInstance字段
// mInstance就是原始的ClassB2Interface对象
Object mInstance = on(gDefault).get("mInstance");
// 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> classB2Interface = on("com.calvin.testreflection.ClassB2Interface").type();
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { classB2Interface },
new ClassB2Mock(mInstance));
on(gDefault).set("mInstance", proxy);
jOOR中对于final类型的反射时,会去修改Field.class的modifiers
字段, 但Android SDK中没有该字段,所以会报NoSuchFieldException异常,但是源码中已经捕获了该异常。
4. 反射语法封装
考虑封装一个ReflectInvoke类来对反射语法进行封装
4.1 反射构造方法并实例化
//无参
public static Object createObject(String className) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//无参
public static Object createObject(Class clazz) {
Class[] pareTyple = new Class[]{};
Object[] pareVaules = new Object[]{};
return createObject(clazz, pareTyple, pareVaules);
}
//一个参数
public static Object createObject(String className, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{ pareTyple };
Object[] pareVaules = new Object[]{ pareVaule };
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//一个参数
public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{ pareTyple };
Object[] pareVaules = new Object[]{ pareVaule };
return createObject(clazz, pareTyples, pareVaules);
}
//多个参数
public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//多个参数
public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {
try {
Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
ctor.setAccessible(true);
return ctor.newInstance(pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
4.2 反射并调用实例方法
//多个参数
public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {
if (obj == null)
return null;
try {
//调用一个private方法
Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples); //在指定类中获取指定的方法
method.setAccessible(true);
return method.invoke(obj, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//一个参数
public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {
Class[] pareTyples = {pareTyple};
Object[] pareVaules = {pareVaule};
return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
}
//无参
public static Object invokeInstanceMethod(Object obj, String methodName) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
}
4.3 反射并调用静态方法
//无参
public static Object invokeStaticMethod(String className, String method_name) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
}
//一个参数
public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{pareTyple};
Object[] pareVaules = new Object[]{pareVaule};
return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
}
//多个参数
public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {
try {
Class obj_class = Class.forName(className);
return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//无参
public static Object invokeStaticMethod(Class clazz, String method_name) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
}
//一个参数
public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {
Class[] classTypes = new Class[]{classType};
Object[] pareVaules = new Object[]{pareVaule};
return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
}
//多个参数
public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {
try {
Method method = clazz.getDeclaredMethod(method_name, pareTyples);
method.setAccessible(true);
return method.invoke(null, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
4.4 获取一个字段的值
public static Object getFieldObject(Object obj, String filedName) {
return getFieldObject(obj.getClass(), obj, filedName);
}
public static Object getFieldObject(String className, Object obj, String filedName) {
try {
Class obj_class = Class.forName(className);
return getFieldObject(obj_class, obj, filedName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static Object getFieldObject(Class clazz, Object obj, String filedName) {
try {
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//获取静态字段
public static Object getStaticFieldObject(String className, String filedName) {
return getFieldObject(className, null, filedName);
}
//获取静态字段
public static Object getStaticFieldObject(Class clazz, String filedName) {
return getFieldObject(clazz, null, filedName);
}
4.5 设置一个字段的值
public static void setFieldObject(Object obj, String filedName, Object filedVaule) {
setFieldObject(obj.getClass(), obj, filedName, filedVaule);
}
public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {
try {
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
field.set(obj, filedVaule);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {
try {
Class obj_class = Class.forName(className);
setFieldObject(obj_class, obj, filedName, filedVaule);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//设置静态字段
public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {
setFieldObject(classname, null, filedName, filedVaule);
}
//设置静态字段
public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {
setFieldObject(clazz, null, filedName, filedVaule);
}
4.6 泛型反射改进
对前面的单类泛型反射使用ReflectInvoke类进行改进,代码如下:
//获取AMN的gDefault单例gDefault,gDefault是静态的
Object gDefault = RefInvoke.getStaticFieldObject("com.calvin.testreflection.AMN", "gDefault");
Class clazz = gDefault.getClass();
Class clazz2 = gDefault.getClass().getSuperclass();
// gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的mInstance字段
Object rawB2Object = RefInvoke.getFieldObject(
"com.calvin.testreflection.Singleton",
gDefault, "mInstance");
// 创建一个这个对象的代理对象ClassB2Mock, 然后替换这个字段, 让我们的代理对象帮忙干活
Class<?> classB2Interface = Class.forName("com.calvin.testreflection.ClassB2Interface");
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[] { classB2Interface },
new ClassB2Mock(rawB2Object));
//把Singleton的mInstance替换为proxy
RefInvoke.setFieldObject("com.calvin.testreflection.Singleton", gDefault, "mInstance", proxy);
小结
总结了反射语法三种编写方式:
- Java基本反射语法。
- jOOR语法。
- 基本反射语法封装。