Java及Android反射详解

1.简介

反射是Java强大的技术,可以反射一个类、反射方法(构造函数)、反射字段。一个比较好的开源实现是jOOR,但是不适用于Android定义为final的字段。
在反射语法中最难的莫过于反射一个泛型类。
反射包括一下技术:

  • 根据一个字符串得到一个类的对象。
  • 获取一个类的所有公有或者私有、静态或实例的字段、方法、属性。
  • 对泛型类的反射。

2. 基本反射语法

2.1 根据一个字符串得到一个类

  1. getClass
String str = “abc”;
Class cl = str.getClass();
  1. Class.forName
    通过字符串获取一个类型
Class c2 = Class.forName("java.lang.String");
Class c3 = Class.forName("android.widget.Button");

//通过getSuperClass,每个Class都有这个函数
Class c5 = c3.getSuperclass(); //得到TextView
  1. 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;
  1. 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 获取类构造函数

构造函数包括privatepublic两种,也支持无参数和有参数两种类型,下面的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)调用构造函数
通过反射调用类的构造函数得到类的实例,这要借助于ConstructornewInstance方法:

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);

小结

总结了反射语法三种编写方式:

  1. Java基本反射语法。
  2. jOOR语法。
  3. 基本反射语法封装。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值