关于Java反射机制的文章很多,这次换种方式来讲解反射的作用。

本文涉及到的知识点:

class.getDeclaredXXX()XXX.getModifiers()method.getReturnType()

method.getParameterTypes()method.isAnnotationPresent(XXX.class)

Modifier.isStatic(method.getModifiers())constructor.newInstance(XX)

先来看一个熟悉的 Class

首先,简单来说,反射就是在运行时可以获取任意ClassObject内部所有成员属性,如成员变量、成员方法、构造函数和 Annotation。

这次先给出一个大家非常熟悉的ClassUserBean

本文要完成的任务就是,在只有一个UserBean.getClass()的情况下,利用代码打印出其内部所有成员变量、方法,并动态执行内部用 @Invoke 修饰的成员方法

package com.wingjay.reflection;

public class UserBean{

    public String userName;

    privatelong userId;

    public UserBean(String userName, long userId){
        this.userName = userName;
        this.userId = userId;
    }

    public String getName(){
        return userName;
    }

    public long getId(){
        return userId;
    }

    @Invoke
    public static void staticMethod(String devName){
        System.out.printf("Hi %s, I'm a static method", devName);
    }

    @Invoke
    public void publicMethod(){
        System.out.println("I'm a public method");
    }

    @Invoke
    private void privateMethod(){
        System.out.println("I'm a private method");
    }
}

在只提供一个 UserBean 的 Class 情况下,

  1. 打印出这个 Class 内部的所有成员变量、成员方法、构造函数,包括 private 的;
  2. 调用这个 Class 内部的三个用 @Invoke 修饰的方法:staticMethod(), publicMethod(), privateMethod();

1. 打印 UserBean Class 里的所有成员变量、成员方法,包括 private 的

首先我们拥有一个Class userBeanClass = UserBean.class,我们要利用这个Class来打印它的成员变量userNameuserId

打印成员变量

那么如何获取成员变量呢,我们发现,Java 里提供了Field这个类来表示成员变量,提供了clazz.getDeclaredFields()来获取一个类内部声明的所有变量。因此,可以利用下面的代码获取userBeanClass内部所有的成员变量。

Field[] fields = userBeanClass.getDeclaredFields();

那么,我们如何将一个field对象打印成private String userName;这种形式呢?或者说如何分别找到privateStringuserName这三个值呢?

其实,Field里包含了三种元素来对应它们,分别是ModifierTypeName。3

private<-- field.getModifiers();
String <-- field.getType();
userName<-- field.getName();

打印结果:

public String userName;
private long userId;

打印成员方法

类似成员变量的Field,成员方法也有对应的类Method,首先可以通过Method[] methods = userBeanClass.getDeclaredMethods();获得所有的成员方法,然后,为了打印形如:public static void staticMethod(String devName)的数据,可以利用下列method提供的方法:

privatestatic<-- method.getModifiers();
void <-- method.getReturnType();
staticMethod <-- method.getName();
String <-- method.getParameterTypes();

因此可以得到:

Method[] methods = userBeanClass.getDeclaredMethods();
for (Methodmethod : methods) {
String methodString = Modifier.toString(method.getModifiers()) + " " ; // private static
    methodString += method.getReturnType().getSimpleName() + " "; // void
    methodString += method.getName() + "("; // staticMethod 
Class[] parameters = method.getParameterTypes();
for (Class parameter : parameters) {
        methodString += parameter.getSimpleName() + " "; // String
    }
    methodString += ")";
System.out.println(methodString);
}

打印结果如下:

public String getName()
public long getId()
public static void staticMethod(String )
public void publicMethod()
private void privateMethod()

可以完整的打印所有成员方法,无论是public还是private,而且能打印static关键字。

打印构造函数

其实构造函数和成员函数非常类似,Java 里提供了Constructor来表示构造函数,为了打印public UserBean(String userName, long userId),可以利用下面的函数实现:

// constructors
Constructor[] constructors = userBeanClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
    String s = Modifier.toString(constructor.getModifiers()) + " ";
    s += constructor.getName() + "(";
    Class[] parameters = constructor.getParameterTypes();
    for (Class parameter : parameters) {
        s += parameter.getSimpleName() + ", ";
    }
    s += ")";
    System.out.println(s);
}

打印结果如下:

public com.wingjay.reflection.UserBean(String, long)

2. 调用 Class 内部的用@Invoke修饰的方法

从上面知道,我们可以利用class.getDeclaredMethods()获取一个类内部所有成员方法,接下来我们还要做的事是

  1. 判断这个方法是否被 @Invoke 修饰
  2. 如果修饰,判断这个方法是不是 static 的
  3. 如果是 static,则可以直接用 class 调用
  4. 如果不是 static,那就需要实例化一个对象来调用
  5. 如果这个方法是 private 的,要记得 setAccessible(true)。

如果分别实现上面的一些功能呢?

  1. 为了判断一个 Method 是否被某个 Annotation 修饰,可以用 method.isAnnotationPresent(Invoke.class) ;
  2. 关于 static private,我们可以用 Modifier 类提供的 Modifier.isStatic() 和 Modifier.isPrivate()来判断;
  3. 如果 Method 不是 static,那就要实例化对象,我们可以用 class.newInstance() 或者 constructor.newInstance(params) 来得到实例。
  4. 关于执行一个 Method,可以使用 method.invoke(object, ...params) 方法,如果方法不是 static 就必须实例化一个 object 传给它,否则可以传 null

实现如下:

Method[] methods = userBeanClass.getDeclaredMethods(); // 获取所有成员方法
for (Method method : methods) {
    if (method.isAnnotationPresent(Invoke.class)) { // 判断是否被 @Invoke 修饰
        if (Modifier.isStatic(method.getModifiers())) { // 如果是 static 方法
            method.invoke(null, "wingjay"); // 直接调用,并传入需要的参数 devName
        } else {
            Class[] params = {String.class, long.class};

            // 获取参数格式为 String,long 的构造函数
            Constructor constructor = userBeanClass.getDeclaredConstructor(params);

            Object userBean = constructor.newInstance("wingjay", 11); // 利用构造函数进行实例化,得到 Object
            if (Modifier.isPrivate(method.getModifiers())) {
                method.setAccessible(true); // 如果是 private 的方法,需要获取其调用权限
            }
            method.invoke(userBean); // 调用 method,无须参数
        }
    }
}

打印结果:

I'm a public method Hi wingjay,
I'm a staticmethod
I'm a private method

可见三个方法都正常调用了,而且public static void staticMethod(String devName)的参数devName也正常传进去了。

小结

基于上面两个实践,我们已经能够利用反射机制,在运行状态下把一个 Class 的内部成员方法、成员变量和构造函数全部获取到,并且能够进行实例化、直接调用内部的成员方法。

因此,有了反射机制,我们即使只有动态得到的 Class,也能直接得到它内部的信息、甚至调用它内部的方法。


public class TestA {
    private TestB mTestB;
    private boolean mTestBoolean = false;
    public TestA(){
        mTestB = new TestB();
    }

    public void getTestBoolean(){
        Log.e("Test","mTestBoolean = "+mTestBoolean);
    }

    private void testPrivateMethod(){
        Log.e("Test","testPrivateMethod!");
    }

    private String testPrivateMethodWithArgs(String args){
        Log.e("Test","testPrivateMethodWithArgs! args = "+args);
        return "return "+args;
    }
}
public class TestB {
    public TestB(){

    }

    private void functionA(){
        Log.e("Test","functionA()");
    }
}

获取属性值

public void getFiled(){
    TestA a = new TestA();

    try {
        Field field = TestA.class.getDeclaredField("mTestBoolean");
        field.setAccessible(true);
        Object value = field.get(a);
        if(value != null && value instanceof Boolean){
            Log.e("Test"," value = "+value);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void getFiled1(){
    TestA a = new TestA();

    try {
        Class classObj = Class.forName("com.example.heqiang.testsomething.TestA");
        Field field = classObj.getDeclaredField("mTestBoolean");
        field.setAccessible(true);
        Object value = field.get(a);
        if(value != null && value instanceof Boolean){
            Log.e("Test"," value = "+value);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

修改属性值

public void setField(){
    TestA a = new TestA();
    a.getTestBoolean();

    try {
        Field field = TestA.class.getDeclaredField("mTestBoolean");
        field.setAccessible(true);
        field.set(a,true);
    } catch (Exception e) {
        e.printStackTrace();
    }

    a.getTestBoolean();
}

调用方法

public void invokeMethod(){
    TestA a = new TestA();
    try {
        Method method = TestA.class.getDeclaredMethod("testPrivateMethod");
        method.setAccessible(true);
        method.invoke(a);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

调用带参数方法并获取返回值

public void invokeMethodWithArgs(){
    TestA a = new TestA();
    Object result = null;
    try {
        Method method = TestA.class.getDeclaredMethod("testPrivateMethodWithArgs",String.class);
        method.setAccessible(true);
        result = method.invoke(a,"Test");
    } catch (Exception e) {
        e.printStackTrace();
    }
    if(result != null && result instanceof String){
        Log.e("Test"," result = "+result);
    }
}

调用私有属性的方法

public void invokePirvateFieldMethod(){
    TestA a = new TestA();
    Method functionA = null;
    try {
        Field field = TestA.class.getDeclaredField("mTestB");
        field.setAccessible(true);
        Object testB = field.get(a);
        if(testB != null){
            Class testBClass = testB.getClass();
            functionA = testBClass.getDeclaredMethod("functionA");
            functionA.setAccessible(true);
            if(functionA != null){
                functionA.invoke(testB);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

以上列举了Java反射的一些使用,下面介绍开源反射工具JOOR的使用。 只需几行代码就实现了前面博客中内部类的反射:

public void testJoor(){
        Reflect.on("com.example.testsomething.OuterClass")//包名
                .create()
                .field("mInnerClass")
                .call("printInt");
        OuterClass outerClass = new OuterClass();
        Reflect.on(outerClass).field("mInnerClass").call("printInt");
        Reflect.on(outerClass).field("mInnerClass").set("mInt", 8);
        Reflect.on(outerClass).field("mInnerClass").call("printInt");
}

Android 中使用

gradle中配置:compile 'org.jooq:joor:0.9.5'

results matching ""

    No results matching ""