4

Java反射之实用篇

 3 years ago
source link: http://yuanfentiank789.github.io/2015/07/19/java-reflection-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

关于Java反射,文章Java反射之基础篇已基本介绍了反射的用法,但是反射的整个调用过程仍比较繁琐,尤其是对于新手,显得比较晦涩。下面介绍些更为简单有效的反射实用内容。

一、反射用法

前面介绍到,反射是为了在运行态能操作类和对象,接下来重点介绍如何反射使用。

对于正常方式来调用方法,往往只需要一行到两行代码,即可完成相应工作。而反射则显得比较繁琐,之所以繁琐仍会才用反射方式,是因为反射能干很多正常实例化对象的方式所无法做到的事。比如操作那些private的类、方法、属性,以及@hide标记过的类、方法、属性。

为了到达即能有反射的功效,同时调用方法简单易用,写了一个ReflectUtils类。对于方法调用,与正常对象的调用过程差不多。主要由以下4类需要用到反射的地方:

  1. 调用类的静态方法
  2. 调用类的非静态方法
  3. set/get类的静态属性
  4. set/get类的非静态属性

1.1 ReflectUtils类用法

调用流程一般为先获取类或对象,再调用相应方法。针对上述4种需求,用法分别如下:

1. 调用类的静态方法
对于参数方法,只需把参数,紧跟方法名后面,可以跟不定长的参数个数。

	Class<?> clazz = ReflectUtils.getClazz("com.yuanhh.model.Outer"); //获取class
	ReflectUtils.invokeStaticMethod(clazz, "outerStaticMethod");  //无参方法
	ReflectUtils.invokeStaticMethod(clazz, "outerStaticMethod","yuanhh"); //有参数方法

2. 调用类的非静态方法

	Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer");  //实例化对象
	ReflectUtils.invokeMethod(obj, "outerMethod");  //无参方法
	ReflectUtils.invokeMethod(obj, "outerMethod", "yuanhh"); //有参方法 **3. set/get类的静态属性**  
  
	ReflectUtils.getStaticField(clazz, "outerStaticField"); //get操作
	ReflectUtils.setStaticField(clazz, "outerStaticField", "new value"); //set操作

4. set/get类的非静态属性

	ReflectUtils.getField(obj, "outerField");  //get操作
	ReflectUtils.setField(obj, "outerField", "new value"); //set操作

如果只知道类名,需先查看该类的所有方法详细参数信息,可以通过调用dumpClass(String className) ,返回值是String,记录着所有构造函数,成员方法,属性值的信息。

1.2 核心代码

关于ReflectUtils类,列表部分核心方法。

先定义一个Outer类, 包名假设为com.yuanhh.model,对于该类,非public,构造方法,成员方法,属性都是private:

class Outer {
    private String outerField = "outer Field";
    private static String outerStaticField = "outer static Field";
    
    private Outer(){
        System.out.println("I'am outer construction without args");
    }
    
    private Outer(String outerField){
        this.outerField = outerField;
        System.out.println("I'am outer construction with args "+ this.outerField);
    }
    
    private void outerMethod(){
        System.out.println("I'am outer method");
    }
    
    private void outerMethod(String param){
        System.out.println("I'am outer method with param "+param);
    }
    
    private static void outerStaticMethod(){
        System.out.println("I'am outer static method");
    }
     
	private static void outerStaticMethod(String param){
        System.out.println("I'am outer static method with param "+param);
    }
}

构造函数,获取类的实例化对象:

/**
 * 实例化获取类名对应的类
 *
 * @param clazz           类
 * @param constructorArgs 构造函数的各个参数
 * @return 实例化对象
 */
public static Object newInstance(Class clazz, Object... constructorArgs) {
    if (clazz == null) {
        return null;
    }

    Object object = null;

    int argLen = constructorArgs == null ? 0 : constructorArgs.length;
    Class<?>[] parameterTypes = new Class[argLen];
    for (int i = 0; i < argLen; i++) {
        parameterTypes[i] = constructorArgs[i].getClass();
    }

    try {
        Constructor constructor = clazz.getDeclaredConstructor(parameterTypes);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        object = constructor.newInstance(constructorArgs);

    } catch (Exception e) {
        e.printStackTrace();
    }

    return object;
}

对象方法的反射调用如下:

/**
 * 反射调用方法
 *
 * @param object     反射调用的对象实例
 * @param methodName 反射调用的对象方法名
 * @param methodArgs 反射调用的对象方法的参数列表
 * @return 反射调用执行的结果
 */
public static Object invokeMethod(Object object, String methodName,
                                  Object... methodArgs) {
    if (object == null) {
        return null;
    }

    Object result = null;
    Class<?> clazz = object.getClass();
    try {
        Method method = clazz.getDeclaredMethod(methodName, obj2class(methodArgs));
        if (method != null) {
            if (!method.isAccessible()) {
                method.setAccessible(true);  //当私有方法时,设置可访问
            }
            result = method.invoke(object, methodArgs);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    return result;

}

对象属性值的反射获取方法:

/**
 * 反射调用,获取属性值
 *
 * @param object    操作对象
 * @param fieldName 对象属性
 * @return 属性值
 */
public static Object getField(Object object, String fieldName) {
    if (object == null) {
        return null;
    }

    Object result = null;
    Class<?> clazz = object.getClass();
    try {
        Field field = clazz.getDeclaredField(fieldName);
        if (field != null) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            result = field.get(object);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;
}

类属性的反射设置过程:

/**
 * 反射调用,设置属性值
 *
 * @param clazz    操作类
 * @param fieldName 属性名
 * @param value     属性的新值
 * @return 设置是否成功
 */
public static boolean setStaticField(Class clazz, String fieldName, Object value) {
    if (clazz == null) {
        return false;
    }

    Object result = null;
    try {
        Field field = clazz.getDeclaredField(fieldName);
        if (field != null) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            field.set(null, value);
        }
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

二、内部类的反射用法

对于内部类,这里比较复杂,而内部类又分static内部类与非static内部类,两者的反射方式还是有区别的,刚开始在这边折腾了好一阵子,一直反射失败。static内部类与非static内部类的反射调用,根本区别在于构造方法不一样。下面通过代码来告诉如何正确。

2.1 static与非static内部类的反射差异

先定义一个包含两个内部类的类:

class Outer {
    /**
     *  普通内部类
     */
    class Inner {
        private String innerField = "inner Field";
        
        private Inner(){
            System.out.println("I'am Inner construction without args");
        }
        
        private Inner(String innerField){
            this.innerField = innerField;
            System.out.println("I'am Inner construction with args "+ this.innerField);
        }
        
        private void innerMethod(){
            System.out.println("I'am inner method");
        }
    }
    
    /**
     * 静态内部类
     */
    static class StaticInner {
        
        private String innerField = "StaticInner Field";
        private static String innerStaticField = "StaticInner static Field";
        
        private StaticInner(){
            System.out.println("I'am StaticInner construction without args");
        }
        
        private StaticInner(String innerField){
            this.innerField = innerField;
            System.out.println("I'am StaticInner construction with args "+ this.innerField);
        }
        
        private void innerMethod(){
            System.out.println("I'am StaticInner method");
        }
        
        private static void innerStaticMethod(){
            System.out.println("I'am StaticInner static method");
        }
    }
}

对于上面两个内部类,如果直接实例化内部类,该怎么做,抛开private等权限不够的问题,应该是这样的:

  • 静态内部类:Outer.StaticInner sInner = new Outer.StaticInner();
  • 非静态内部类: Outer.Inner inner = new Outer().new Inner();

这种差异,在于内部类的构造方法不一样。我们可以通过下面的方法dumpClass()来比较。

/**
 * 获取类的所有 构造函数,属性,方法
 *
 * @param className 类名
 * @return
 */
public static String dumpClass(String className) {
    StringBuffer sb = new StringBuffer();
    Class<?> clazz;
    try {
        clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        return "";
    }

    Constructor<?>[] cs = clazz.getDeclaredConstructors();
    sb.append("------  Constructor  ------>  ").append("\n");
    for (Constructor<?> c : cs) {
        sb.append(c.toString()).append("\n");
    }

    sb.append("------  Field  ------>").append("\n");
    Field[] fs = clazz.getDeclaredFields();
    for (Field f : fs) {
        sb.append(f.toString()).append("\n");
        ;
    }
    sb.append("------  Method  ------>").append("\n");
    Method[] ms = clazz.getDeclaredMethods();
    for (Method m : ms) {
        sb.append(m.toString()).append("\n");
    }
    return sb.toString();
}

通过dumpClass(),对比我们会发现,

  • static内部类的默认构造函数: private void com.yuanhh.model.Outer$StaticInner.innerMethod()
  • 非static内部类的默认构造函数: private com.yuanhh.model.Outer$Inner(com.yuanhh.model.Outer),多了一个参数com.yuanhh.model.Outer,也就是说非static内部类保持了外部类的引用。从属性,我们也会发现多了一个final属性final com.yuanhh.model.Outer com.yuanhh.model.Outer$Inner.this$0,这正是用于存储外部类的属性值。

正是这差异,导致两者的反射调用过程中构造方法的使用不一样。另外内部类的类名使用采用$符号,来连接外部类与内部类,格式为outer$inner。

2.2 static内部类的 ReflectUtils类用法

  1. 调用类的静态方法(先获取类,再调用)

     Class<?> clazz = ReflectUtils.getClazz("com.yuanhh.model.Outer$StaticInner"); //获取class
     ReflectUtils.invokeStaticMethod(clazz, "innerStaticMethod");  //无参方法
     ReflectUtils.invokeStaticMethod(clazz, "innerStaticMethod","yuanhh"); //有参数方法
    
  2. 调用类的非静态方法(先获取对象,再调用)

     Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer$StaticInner");  //实例化对象
     ReflectUtils.invokeMethod(obj, "innerMethod");  //无参方法
     ReflectUtils.invokeMethod(obj, "innerMethod", "yuanhh"); //有参方法
    
  3. set/get类的静态属性(先获取类,再调用)

     ReflectUtils.getStaticField(clazz, "innerField"); //get操作
     ReflectUtils.setStaticField(clazz, "innerField", "new value"); //set操作
    
  4. set/get类的非静态属性(先获取对象,再调用)

     ReflectUtils.getField(obj, "innerField");  //get操作
     ReflectUtils.setField(obj, "innerField", "new value"); //set操作 ### 2.2 非static内部类的 `ReflectUtils`类用法 非static内部类,不能定义静态方法和静态属性,故操作只有两项:
    
  5. 调用类的非静态方法(先获取对象,再调用)

     // 获取外部类实例,这是static内部类所不需要的,注意点
     Object outObj = ReflectUtils.newInstance("com.yuanhh.model.Outer"); 
     Object obj = ReflectUtils.newInstance("com.yuanhh.model.Outer$Inner", outObj);  //实例化对象
     ReflectUtils.invokeMethod(obj, "innerMethod");  //无参方法
     ReflectUtils.invokeMethod(obj, "innerMethod", "yuanhh"); //有参方法
    
  6. set/get类的非静态属性(先获取对象,再调用)

     ReflectUtils.getField(obj, "innerField");  //get操作
     ReflectUtils.setField(obj, "innerField", "new value"); //set操作
    
  • 主要ReflectUtils类的用法,只需要按1.1 ReflectUtils类用法的方式使用即可,比如反射调用方法,只需知道类与方法名,即可调用完成invokeMethod(Object object, String methodName)操作简单。
  • static与非static内部类的区别,在2.2与2.3这两节,对于内部类差异仅仅在于传递参数多了一个$符号,以及非static内部类实例化需要加上外部类的实例。
  • ReflectUtils类,以及本文所有涉及的代码,即将打包上传。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK