Java学习 第5章 反射

隐藏

反射

能够分析类的能力的程序称为反射(reflective)。 作用当然是用于动态分析代码咯。 缺点是安全性差,有可能在Debug阶段运行良好,但到客户虚拟机上却出错, 所以也提出应用程序中尽量不用反射。

Class类

程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时类型标识。 该标识跟踪每个对象所属类信息。

Java中可以通过专门的Class类访问这些信息,Class类中保存这些信息。

获得Class类的三种方法

例如:

Employee e;
//获得Class的方法1: getClass()方法将返回一个Class类型的实例,
Class cl = e.getClass();

//获得Class的方法2: 通过forName静态方法
String className = "java.util.Date" //如果不是类将会throw异常
Class cl = Class.forName(className);

//获得Class的方法3: T.class
Class cl = int.Class;

利用反射分析类的例子

在java.lang.reflect包中有三个类分别是:Field、Method、Constructor分别保存了类的域、方法、构造器信息。 如下例子通过反射获得了类的Field、Method和Constructor、超类等全部类信息, 这也是反射机制最重要的内容。 具体参见《Java核心技术I》P196 - P201。不详述。

package reflection;

import java.lang.reflect.*;
import java.util.Objects;
import java.util.Scanner;

/**
 * This program uses reflection to print all features of a class.
 * this code example is from [Core Java 1]#reflection
 */
public class ReflectionTest {
    public static void main(String[] args) {
        String name;
        if(args.length > 0)
            name = args[0];
        else {
            Scanner in = new Scanner(System.in);
            System.out.println("Enter class name(e.g. java.util.Date):");
            name = in.next();
        }

        try {
            //print class name and superclass name(if != Object)
            Class cl = Class.forName(name);
            Class supercl = cl.getSuperclass();
            String modifiers = Modifier.toString(cl.getModifiers());

            //print modifiers
            if(modifiers.length() > 0)
                System.out.print(modifiers + " ");

            //print name
            System.out.print("class" + name);
            //print super class
            if (supercl != null && supercl != Objects.class) {
                //if exist and not the root class Objects
                System.out.print(" extends " + supercl.getName());
            }
            System.out.print("\n{\n");
            printConstructor(cl);
            System.out.println();
            printMethods(cl);
            System.out.println();
            printFields(cl);
            System.out.println("}");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    /**
     * Print all constructor of a class
     * @param cl a class
     */
    public static void printConstructor(Class cl) {
        Constructor[] constructors = cl.getConstructors();
        for (Constructor c : constructors) {
            String name = c.getName();
            System.out.print("    ");//tab空四格
            String modifiers = Modifier.toString(c.getModifiers());
            if(modifiers.length() > 0)
                System.out.print(modifiers + " ");//修饰符 如final static等
            System.out.print(name + "(");

            //print parameter types
            Class[] paramTypes = c.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                if( j > 0 ) System.out.print(",");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * Prints all methods of a class
     * @param cl a class
     */
    public static void printMethods(Class cl) {
        Method[] methods = cl.getDeclaredMethods();
        for (Method m :methods) {
            Class<?> retType = m.getReturnType();
            String name = m.getName();

            System.out.print("    "); //tab 4个空格
            //print modifiers
            String modifiers = Modifier.toString(m.getModifiers());
            if(modifiers.length() > 0)
                System.out.print(modifiers + " "); //修饰符 如final static等
            //print return types and method name
            System.out.print(retType.getName() + " " + name + "(");

            //print parameter types
            Class<?>[] paramTypes = m.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++) {
                if(j > 0) System.out.printf(",");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * Prints all fields of a class
     * @param cl a class
     */
    public static void printFields(Class cl) {
        Field[] fields = cl.getDeclaredFields();

        for (Field f :fields) {
            Class<?> type = f.getType();
            String name = f.getName();
            System.out.print("    "); //tab, 4 spaces
            String modifiers = Modifier.toString(f.getModifiers());
            if(modifiers.length() > 0)
                System.out.print(modifiers + " ");
            System.out.println(type.getName() + " " + name + ";");
        }
    }
}

反射中容易出现异常

由于反射很有可能会出现异常,所以通常需要try-catch配合捕获异常,比如ClassNotFoundException异常等等。

运行时使用反射分析对象

如上可以获得域名,域类型,域的修饰符(static、public等)但没有获得域值。 如需获得域值用aField.get(aClass)方法,比如:

Class cl = getClass(Employee[1]);
Field fieldName = cl.getDeclaredField("name"); //获得名为name的域
Object objName = fieldName.get(Employee[1]); //获得雇员名(即name域的值)
//get函数有些不漂亮,fieldName来自Employee[1]却还需要Employee[1]作为get的参数
//这样有些耦合在一起了,猜测可能是考虑到有些域是间接继承的,这样更明确吧

用setAccessible()方法使get方法获得访问域值的权限

如上由于name域是私有的,所以要用get方法访问还需要用field.getAccessible(true)提权。

使用反射编写泛型方法

有时候为了编写适用于多种Class类型的方法会用到获取Class实际类型的办法实现。

调用任意函数

C/C++的函数指针对应Java中的Method对象

在C/C++的函数指针对应Java中的Method对象中可以用函数指针执行任意函数。 而Java设计者则认为方法指针很危险,所以Java没有提供方法指针, 而是采用接口的方式。 反射机制则可以实现调用任意方法。(注:用代理才上调用任意方法吧?。。。)

我猜测过程就是,先通过反射获得一个类类型,然后new一个一样类型的类,并在类中调用。

而答案是… Java的Method类提供一个invoke方法,允许调用包装在Method对象中的方法。

而用Method类的invoke方法比较慢,并不是最好的选择,建议使用接口进行回调,代码将更快,更易于维护。

-----EOF-----

Categories: java Tags: Java