Java Reflection 相关及示例

来源:互联网 发布:51单片机的引脚功能 编辑:程序博客网 时间:2024/06/11 23:08

Java Reflection 相关及示例


前言:

 

        代码有点长、贴出github地址:https://github.com/andyChenHuaYing/scattered-items/tree/master/items-java-reflection

        测试目标类:TargetClass、自定义的辅助类比较多、在这里不贴了。篇幅有限、并且测试也简单、因此测试类也没有提及。

 

一:简介

 

         Java Reflection是针对Class也就是我们平常说的类而言的、用于操作Java中的Class、在Java中万事万物皆对象(需要注意的是原始类型和静态类、静态方法不是面向对象的、它是属于类的)、那么“类”也是Java中的对象、是“Class类”类型的对象。

 

二:反射相关

 

        Java Reflection相关的是四个final类型的类Class、Method、Field、Constructor。以及java中所有类的父类Object。其中Class的构造方法是私有的、也就意味着不允许我们外部调用Class类构造方法来构造Class实例、Class类提供了对于Java中类类型的操作、比如根据类类型获取以及操作属性、方法(包括私有方法、私有类型、至于这种行为的应不应该暂不讨论、但是既然现在存在、自然有存在的道理)、ElementType.TYPE,ElementTYPE.METHOD, ElementTYPE.FIELD级别的Annotation信息等。

        Java类中的属性也是对象——Field、同样普通方法也是——Method。构造方法——Constructor、Field、Method、Constructor指定类类型中属性对象、普通方法对象和构造方法对象的表示类型、用于操作指定类类型中的属性和方法。

 

三:类的生命周期

 

        在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。

装载:类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载一次。

        链接:链接就是把二进制数据组装为可以运行的状态。链接分为校验,准备,解析这3个阶段。校验一般用来确认此二进制文件是否适合当前的JVM(版本),准备就是为静态成员分配内存空间,并设置默认值。解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)。

        初始化:完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。

        当没有任何引用指向Class对象时就会被卸载,结束类的生命周期。

 

四:类的三种表示方法

 

        假设我们现在有一个类Foo、那么他的类类型可以通过下面这三种方式来获取:

        1、 Classclazz1 = Foo.class;        2、 Classclazz2 = foo.getClass();        3、 Classclazz3 = Class.forName(“packageName.Foo”);

        前面中知道、每个类只会被装载一次、也就是说每个类不管是通过哪种方式获取的类类型、都是一样的:

        clazz1 == clazz2 == clazz3;

五:静态加载和动态加载

 

        这两个名词我们都不陌生、但是通过反射我们更能认清他们的区别。还是再重复一遍两者的概念:

        1、 静态加载:表示编译时(javac)加载类。

        2、 动态加载:表示运行时(java)加载类。

        与之对应的、我们可以结合RuntimeException和非RuntimeException来理解。非RuntimeException就在编译时必须处理的异常、比如常见的:ClassNotFoundException、ClassCastException、IOException等等、这些在我们敲代码的时候就必须要指明是抛出还是使用try、catch来捕获。否则的话就是编译失败、而RuntimeException则不需要我们在编译的时候来处理、而是在运行时如果不满足条件就会抛出异常、比如常见的:NullPointException、IndexOutofBoundException等等。

        Java中所有使用new关键字构造的类实例都是在编译期加载的、也就意味着当编译我们写好的Java类时、所有与此类相关的并且使用new关键字实例化的都必须存在。一并编译成class文件。

        而动态加载则是在程序运行时、使用到的时候再根据实际的类型去加载类的实例。

 

六:三种获取Class方式的区别

 

        1、 List.class、是在编译时就可以获取Foo类的类型。

        2、 list.getClass()是在运行时根据Foo的一个具体的实例对象来获取其Class类型、比如他有可能是ArrayList类型的、也有可能是LinkedList、更有可能是TreeList类型的。这只有在运行时才能确定具体是哪一种类型。

        3、 Class.forName(“packageName.ClassName”)不仅表示了类的类型、还代表了动态加载类。


七:实例前的说明

 

        反射的操作都是在编译之后进行的、也就是说在运行时执行的。可以根据一个具体的Java类类型来创建此类的实例、前提是此类必须要有无参构造方法!


八:通过Java Reflection 绕过泛型

     

        对于Generic(泛型)我们都不陌生、尤其是在使用集合的时候、指定泛型可以避免我们添加错误的类型进去、导致意想不到的结果、Generic是在编译时规定我们只能添加同一种类型数据。但是通过反射我们却可以绕过泛型的校验、通过编译。

        代码:


package org.alien.reflection.generic;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;/** * Demonstrate java reflect could bypass the Collection Generic. * Happy day, happy life. * * @author andy * @version 1.0-SNAPSHOT *          Created date: 2014-12-22 20:46 */@SuppressWarnings("unchecked")public class CollectionGenericEssence {    /**     * Use java reflect to bypass the Collection Generic.     * @param arrayList     *        ArrayList of String.     * @param value     *        An object which will be added in ArrayList of String.     */    public ArrayList<String> addElementsByMethodReflect(ArrayList<String> arrayList, Object value) {        /*         * Illegal value type:         * arrayList.add(value);         */        Class arrayListClass = arrayList.getClass();        try {            Method method = arrayListClass.getMethod("add", Object.class);            method.invoke(arrayList, value);        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }        return arrayList;    }    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {        ArrayList<String> arrayList = new ArrayList<String>();        Class clazz = arrayList.getClass();        Method method = clazz.getMethod("add", Object.class);        method.invoke(arrayList, 20);        System.out.println(arrayList.size());    }    @Override    public String toString() {        return "CollectionGenericEssence{}";    }}

        总结:Java中泛型是防止输入错误类型的一种措施、通过上面代码也可以说明编译之后集合的泛型就已经没有作用了。也就是去泛型化的、只在编译时期有用。当然、我们也就不能使用foreach来迭代这种特殊集合了。

 

九:通过Java Reflection 来重构一个类

 

        首先说明:这样做的意义是通过他来知晓JavaReflection相关类的使用、这些是基础、同时也不是全部、侧重点不同。有了下面的这些过滤、以后用到这些之外的也不外乎寻找的结果不同、过程都是相同的。对于Field、Method的执行会在后面有。同时下面重现的类也不包括方法体、暂时只包括本类的属性、方法。以下分成几点来说明、并且这些都在代码中有注释、且所有的方法都可以单独测试、测试类的代码就不贴了、后面会有TargetClass内容、里面使用到的自定义Annotation也不再贴、都放在github上、前后会贴出地址。具体过程:

        1. 获取完成包名、

        2. 关于import、当类中使用的类都是全名时、就不需要import(当然方法体中除外)

        3. 获取类级别注释、

        4. 获取类的父类、

        5. 获取类的所有接口、

        6. 获取类修饰符、

        7. 获取类名、

        8. 获取类中所有属性、

        9. 分别获取属性的所有Annotation、

        10. 获取所有构造方法、

        11. 获取所有构造方法的Annotation、

        12. 获取所有方法、

        13. 分别获取方法的Annotation、

        14. 分别获取所有方法的修饰符、

        15. 分别获取所有方法的返回值、

        16. 分别获取所有方法的所有参数以及类型、

        17. 分别获取所有方法声明的异常信息。

        18. 综上组合成最终结果。

        具体代码:

     

package org.alien.reflection.api;import java.lang.annotation.Annotation;import java.lang.reflect.*;/** * Happy day, happy life. * * @author andy * @version 1.0-SNAPSHOT *          Created date: 2014-12-22 21:48 */public class ShowClassDetailInfo {    private static final String LINE_BREAK = "\r\n";    private static final String SPACE = " ";    private static final String SEMICOLON = ";";    public static String showClassFullInfo(Class clazz) {        StringBuilder stringBuilder = new StringBuilder();        stringBuilder.append("package ").append(showClassPackage(clazz)).append(LINE_BREAK);        String classModifier = Modifier.toString(clazz.getModifiers());        stringBuilder.append(classModifier).append(SPACE).append(clazz.getSimpleName());        if (hasSuperClass(clazz)) {            stringBuilder.append(SPACE).append("extend ").append(clazz.getSimpleName());        }        if (hasInterface(clazz)) {            stringBuilder.append(SPACE).append("implement ").append(showClassImplInterfaces(clazz));        }        stringBuilder.append(" {").append(LINE_BREAK);        stringBuilder.append(showDeclaredField(clazz))                .append(LINE_BREAK)                .append(showConstructs(clazz))                .append(LINE_BREAK)                .append(showDeclaredMethod(clazz))                .append(LINE_BREAK)                .append("}");        return stringBuilder.toString();    }    /**     * Show target class's package name.     * @return     *      Target class package name.     */    public static String showClassPackage(Class clazz) {        return clazz.getPackage().getName();    }    /**     * Validate target class has super class or not.     * @param clazz     *        Target class.     * @return     *        If target class has super class, return true, otherwise false.     */    public static boolean hasSuperClass(Class clazz) {        Class superclass = clazz.getSuperclass();        return  superclass != null;    }    /**     * Show target class's super class.     * @param clazz     *        Target class.     * @return     *        The name of super class.     */    public static String showSuperClass(Class clazz) {        if (hasSuperClass(clazz)) {            return clazz.getSuperclass().getSimpleName();        }        return "Object";    }    /**     * Validate target class has interface or not.     * @param clazz     *        Target class.     * @return     *        If target class has one or more interface, return true, otherwise false.     */    public static boolean hasInterface(Class clazz) {        return clazz.getInterfaces().length > 0;    }    /**     * Show target class's interfaces.     * @param clazz     *        Target class     * @return     *        Interfaces info.     */    public static String showClassImplInterfaces(Class clazz) {        StringBuffer stringBuffer = new StringBuffer();        Class[] classes = clazz.getInterfaces();        if (hasInterface(clazz)) {            for (Class face : classes) {                stringBuffer.append(face.getSimpleName()).append(", ");            }            stringBuffer = fixStringBuffer(stringBuffer);        }        return stringBuffer.toString();    }    /**     * Show target class's constructors     * @param clazz     *        Target class     * @return     *        Constructors info.     */    public static String showConstructs(Class clazz) {        StringBuffer stringBuffer = new StringBuffer();        Constructor[] constructors = clazz.getConstructors();        for (Constructor constructor : constructors) {            Annotation[] annotations = constructor.getDeclaredAnnotations();            stringBuffer = constructorAnnotation(stringBuffer, annotations);            String modifierType = Modifier.toString(constructor.getModifiers());            stringBuffer.append(modifierType).append(SPACE).append(clazz.getSimpleName()).append("(");            Class[] types = constructor.getParameterTypes();            if (types.length > 0) {                for (Class type : types) {                    String parameterTypeName = type.getName();                    String parameterTypeReferenceName = type.getClass().getSimpleName().toLowerCase();                    stringBuffer = injectMethodParametersContent(stringBuffer,                            parameterTypeName, parameterTypeReferenceName);                }                stringBuffer = fixStringBuffer(stringBuffer);            }            stringBuffer.append(")").append("{...}").append(LINE_BREAK).append(LINE_BREAK);        }        return stringBuffer.toString();    }    /**     * Show all fields value declared by target class instance.     * @param object     *        Target class instance.     * @return     *        The object array of fields value.     * @throws IllegalAccessException     *        Execution failed.     */    public static Object[] showAllDirectInstanceFieldsValue(Object object) throws IllegalAccessException {        Field[] fields = object.getClass().getDeclaredFields();        Object[] objects = new Object[fields.length];        for (int i = 0; i < fields.length; i++) {            Field field = fields[i];            /*            change the access privilege so that we could obtain or change the private Field ,            Constructor(except Class) or private method action.            */            field.setAccessible(true);            Object obj = fields[i].get(object);            objects[i] = obj;            System.out.println(fields[i].get(object));            System.out.println(Modifier.toString(fields[i].getModifiers()));        }        return objects;    }    /**     * Show class's declared field.     * @param clazz     *          Target class.     * @return     *          Declared field info.     */    public static String showDeclaredField(Class clazz) {        StringBuffer stringBuffer = new StringBuffer();        Field[] fields = clazz.getDeclaredFields();        for (Field field : fields) {            Annotation[] annotations = field.getDeclaredAnnotations();            stringBuffer = constructorAnnotation(stringBuffer, annotations);            String fieldModifier = Modifier.toString(field.getModifiers());            String fieldType = field.getType().getSimpleName();            stringBuffer.append(fieldModifier).append(SPACE).append(fieldType).append(SPACE);            stringBuffer.append(field.getName()).append(SEMICOLON).append(LINE_BREAK).append(LINE_BREAK);        }        return stringBuffer.toString();    }    /**     * Show class's declared method.     * @param clazz     *        Target Class type.     */    public static String showDeclaredMethod(Class clazz) {        Method[] methods = clazz.getDeclaredMethods();        return showMethodsDetail(methods);    }    /**     * Show methods' detail information     * @param methods     *      Target class's method.     * @return     *      All methods print info .     */    private static String showMethodsDetail(Method[] methods) {        StringBuffer stringBuffer = new StringBuffer();        for (Method method : methods) {            //Construct Annotation            Annotation[] annotations = method.getDeclaredAnnotations();            stringBuffer = constructorAnnotation(stringBuffer, annotations);            //Construct method modifier type            String modifierType = Modifier.toString(method.getModifiers());            stringBuffer.append(modifierType).append(SPACE);//            String methodInfo = method.toString();//            if (methodInfo.startsWith("public")) {//                stringBuffer.append("public ");//            }//            if (methodInfo.startsWith("protected")) {//                stringBuffer.append("protected ");//            }//            if (methodInfo.startsWith("private")) {//                stringBuffer.append("private ");//            }            //Construct method name            Class<?> returnType = method.getReturnType();            String methodName = method.getName();            stringBuffer.append(returnType).append(SPACE).append(methodName).append("(");            //Construction method parameters            Class[] parameterTypes = method.getParameterTypes();            if (parameterTypes.length > 0) {                for (Class parameterTypeClass : parameterTypes) {                    String parameterTypeName = parameterTypeClass.getName();                    String parameterTypeReferenceName = parameterTypeClass.getSimpleName().toLowerCase();                    stringBuffer = injectMethodParametersContent(stringBuffer,                            parameterTypeName, parameterTypeReferenceName);                }                stringBuffer = fixStringBuffer(stringBuffer);            }            stringBuffer.append(")");            //Construct throws Exceptions            Class<?>[] exceptionTypes = method.getExceptionTypes();            if (exceptionTypes.length > 0) {                stringBuffer.append("throws ");                for (Class exceptionType : exceptionTypes) {                    String exceptionName = exceptionType.getSimpleName();                    stringBuffer.append(exceptionName).append(", ");                }                stringBuffer = fixStringBuffer(stringBuffer);            }            //Construct method body, of course is invisible.            stringBuffer.append("{...}").append(LINE_BREAK).append(LINE_BREAK);        }        return stringBuffer.toString();    }    /**     * Construct target's annotation expression.     * @param stringBuffer     *        Result container.     * @param annotations     *        Target's all annotations.     * @return     *        Final expression.     */    private static StringBuffer constructorAnnotation(StringBuffer stringBuffer, Annotation[] annotations) {        if (annotations.length > 0) {            for (Annotation annotation : annotations) {                String annotationName = annotation.annotationType().getSimpleName();                stringBuffer.append("@").append(annotationName).append("\r\n");            }        }        return stringBuffer;    }    /**     * Cut the last "," in stringBuffer.     * @param stringBuffer     *        raw str.     * @return     *        Fixed str.     */    private static StringBuffer fixStringBuffer(StringBuffer stringBuffer) {        return stringBuffer.delete(stringBuffer.lastIndexOf(","), stringBuffer.length());    }    /**     * Construct method parameters list.     * @param stringBuffer     *        Parameters list container.     * @param parameterTypeName     *        Parameter type class name.     * @param parameterTypeReferenceName     *        Parameter dummy reference name.     * @return     *        Method parameters list info.     */    private static StringBuffer injectMethodParametersContent(StringBuffer stringBuffer, String parameterTypeName, String parameterTypeReferenceName) {        return stringBuffer.append(parameterTypeName).append(SPACE).append(parameterTypeReferenceName).append(", ");    }}

十: 方法、属性的执行

     

        1、方法的执行只有下面一个方法、可以自己动手试一下:

                Method.invok(TargetClassInstance, Object …parameters);

        2、 属性的执行方法类似、具体可以看一下Field的API:

                Object = field.getValue(TargetClassInstancetarget);

        重点说明私有方法、属性的执行赋值。

        Java中对类、方法、属性使用修饰符Public、protected、 默认、 private 来修饰限定一个类、属性、方法的访问权限。而JavaReflection中不但有对类类型以及他的组成部分的操作方式、还提供了修改类类型中的属性、方法的访问权限的权利。AccessibleObject、他是Field、Method、Constructor、ReflectPermission的父类、其中有一个方法:setAccessible()、可接收一个boolean类型参数、来指定是否开放当前Field、Method、Constructor的私有访问的权利。注意(前面提到过Class的构造方法是私有的、是不是可以通过这种方式来获取Class类私有构造方法的权利?答案是否定的、因为Class并不是AccessibleObject的子类!)。

        代码片段:

 

    public static Object[] showAllDirectInstanceFieldsValue(Object object) throws IllegalAccessException {        Field[] fields = object.getClass().getDeclaredFields();        Object[] objects = new Object[fields.length];        for (int i = 0; i < fields.length; i++) {            Field field = fields[i];            /*            change the access privilege so that we could obtain or change the private Field ,            Constructor(except Class) or private method action.            */            field.setAccessible(true);            Object obj = fields[i].get(object);            objects[i] = obj;            System.out.println(fields[i].get(object));            System.out.println(Modifier.toString(fields[i].getModifiers()));        }        return objects;    }

补充:


        这里没有类加载器的信息、类加载器牵涉到较多的JVM内容、暂不表。最后放上类结构图以及本项目在github上的地址:

https://github.com/andyChenHuaYing/scattered-items/tree/master/items-java-reflection

        类结构图:

     

     

     

 

 

 

      

1 0
原创粉丝点击