[JavaSE]反射

来源:互联网 发布:微商授权系统源码 编辑:程序博客网 时间:2024/04/29 16:29

这两天一直看JavaSE中反射部分,当然看的是佟刚老师的视频,总算是入门了,这里将通过代码【源码地址】形式总结反射的基本用法。

在说什么是反射之前,先说什么是Class 类

一、Class 类

  • Class 是一个类
  • 对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。
  • 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一 个 Class 对象包含了特定某个类的有关信息。
  • Class 对象只能由系统建立对象
  • 一个类在 JVM 中只会有一个Class实例
    /**     * 如何得到 Class 对象     * 1. 通过 类名.class 的方式.      * 2. 通过对象调用 getClass() 方法的方式.     * 3. 通过全类名的方式.     */    @Test    public void testClass() throws ClassNotFoundException{        Class clazz = null;        //1. 通过 类名.class 的方式.        clazz = Person.class;        //2. 通过对象调用 getClass() 方法的方式.        Person person = new Person();        clazz = person.getClass();        //3. 通过全类名的方式.        String className = "henan.huel.java.Person";        clazz = Class.forName(className);        System.out.println(clazz);    }
    /**     * 通过Class 对象的 newInstance() 方法来创建类的一个对象,实际调用的是类的那个无参数的构造器.     * 一般地, 一个类若声明了带参数的构造器, 也要声明一个无参数的构造器.     */    @Test    public void testNewInstance() throws ClassNotFoundException,         InstantiationException, IllegalAccessException{        Class clazz = Class.forName("henan.huel.java.Person");        Object obj = clazz.newInstance();        System.out.println(obj);    }

二、ClassLoader

 类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

这里写图片描述
引导类加载器(Bootstap ClassLoader):用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
扩展类加载器(Extension ClassLoader):负责jdk_home/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库 。
系统类加载器(System ClassLoader):负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 。

    /**     * 关于ClassLoader:     * 1. 获取一个系统类加载器     * 2. 获取系统类加载器的父类加载器, 即扩展类加载器     * 3. 获取扩展类加载器的父类加载器, 即引导类加载器     * 4. 测试的当前类由哪个类加载器进行加载     * 5. 测试 JDK 提供的Object 类由哪个类加载器负责加载     * 6. 关于类加载器的一个主要方法, getResourceAsStream()     */    @Test    public void testClassLoader() throws ClassNotFoundException{        //1. 获取一个系统类加载器        ClassLoader loader = ClassLoader.getSystemClassLoader();        System.out.println(loader);        //2. 获取系统类加载器的父类加载器, 即扩展类加载器        loader = loader.getParent();        System.out.println(loader);        //3. 获取扩展类加载器的父类加载器, 即引导类加载器        loader = loader.getParent();        System.out.println(loader); //null(无法获取)        //4. 测试的当前类由哪个类加载器进行加载        loader = Class.forName("henan.huel.java.ReflectionTest")                    .getClassLoader();        System.out.println(loader);//系统类加载器        //5. 测试 JDK 提供的Object 类由哪个类加载器负责加载        loader = Class.forName("java.lang.Object")                    .getClassLoader();        System.out.println(loader);//引导类加载器        //6. 关于类加载器的一个主要方法, getResourceAsStream()        //调用getResourceAsStream 获取类路径下的文件对应的输入流.        InputStream in = this.getClass().getClassLoader()                    .getResourceAsStream("test.properties");        System.out.println(in);    }

三、反射

反射概述:
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)
  • 生成动态代理

1. 关于Method

如何获取和调用Method:

    /**     * 关于Method:     * 1. 如何获取类的方法的数组     * 2. 如何获取指定的方法,      *      2.1 getMethod(String name, Class ... parameterTypes)、     *      2.2 getDeclaredMethod(String name, Class ... parameterTypes):     *          name: 方法名     *          parameterTypes: 参数类型     *          Class ... : 长度可变     * 3. 通过method 对象执行方法:     *      public Object invoke(Object obj, Object... args)     *          obj: 执行那个对象的方法?     *          args: 执行方法时需要传入的参数.     */    @Test    public void testMethod() throws Exception{        Class clazz = Class.forName("henan.huel.java.Person");        //1. 如何获取类的方法的数组:        //1.1 得到clazz 对应的类中有哪些方法, 不能获取private 方法.        Method [] methods = clazz.getMethods();        for(Method method : methods){            System.out.println(method.getName());        }        //1.2 获取所有的方法, 包括private方法. 且只获取当前类声明的方法.        methods = clazz.getDeclaredMethods();        for(Method method : methods){            System.out.println(method.getName());        }        //2. 如何获取指定的方法.        //2.1 同样地, 不能获取private 方法, 这里setName 通过public 修饰        Method method = clazz.getMethod("setName", String.class);        System.out.println(method);        //2.2 这里setName 通过private 修饰        method = clazz.getDeclaredMethod("setName", String.class, Integer.class);        System.out.println(method);        //3. 执行方法.        Object obj = clazz.newInstance();        //要想执行私有方法, 需要先调用Method 的 setAccessible(true) 方法, 使其变为可访问的.        method.setAccessible(true);        method.invoke(obj, "huel", 21);    }

如何获取父类:

    /**     * 获取父类     */    @Test    public void testGetSuperClass() throws ClassNotFoundException{        Class clazz = Class.forName("henan.huel.java.Student");        Class superClass = clazz.getSuperclass();        System.out.println(superClass);    }

工具方法1:无法完成对父类方法的调用(局限)

    /**     *      * @param className: 某个类的全类名     * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法     * @param args: 调用该方法需要传入的参数     * @return     * @throws IllegalAccessException      * @throws InstantiationException      * @throws ClassNotFoundException      */    public Object invoke(String className, String methodName, Object ... args){        Object obj = null;        try {            obj = Class.forName(className).newInstance();        } catch (Exception e) {            e.printStackTrace();        }        return invoke(obj, methodName, args);    }    /**     *      * @param obj: 方法执行的那个对象.     * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法     * @param args: 调用该方法需要传入的参数     * @return     */    public Object invoke(Object obj, String methodName, Object ... args){        //1. 获取Method 对象        //完成由 args 到 parameterTypes的转换        Class [] parameterTypes = new Class[args.length];        for(int i = 0; i < args.length; i++){            parameterTypes[i] = args[i].getClass();        }        try {            Method method = obj.getClass().getDeclaredMethod(methodName, parameterTypes);            //2. 执行Method 方法            //3. 返回方法的返回值            method.setAccessible(true);            method.invoke(obj, args);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    @Test    public void testInvoke(){        Object obj = new Person();        // 执行obj 的setName 方法        invoke(obj, "setName", "huel", 21);        invoke("henan.huel.java.Person", "setName", "huel", 21);    }

工具方法2:完成对父类方法的调用

    /**     * 循环向上转型, 获取对象的 DeclaredMethod     * @param clazz: Class 对象clazz     * @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法, 还可能是该方法在父类中定义的(私有)方法.     * @param parameterTypes: 方法的参数类型(使用Class来描述)的列表     * @return     */    public Method getMethod(Class<?> clazz, String methodName, Class<?> ... parameterTypes){        for(Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass())        try {            Method method = superClass.getDeclaredMethod(methodName, parameterTypes);            return method;        } catch (Exception e) {}        return null;    }    /**     * 完成Object[] 到 Class[] 的转换     * @param args: Object []     * @return: Class []     */    public Class<?> [] getParameterTypes(Object ... args){        Class<?> [] parameterTypes = new Class[args.length];        for(int i = 0; i < args.length; i++){            parameterTypes[i] = args[i].getClass();        }        return parameterTypes;    }    /**     * 执行 obj 对象的方法, 参数为args     * @param obj: 方法执行的那个对象.     * @param method : 获取的 DeclaredMethod     * @param args: 调用该方法需要传入的参数     * @return: 方法的返回值     */    private Object invokeMethod(Object obj, Method method, Object[] args)            throws InstantiationException, IllegalAccessException,            InvocationTargetException {        method.setAccessible(true);        Object result = method.invoke(obj, args);        return result;    }    /**     *      * @param obj: 方法执行的那个对象.     * @param methodName: 的一个方法的方法名.      *      该方法也可能是私有方法, 还可能是该方法在父类中定义的(私有)方法.     * @param args: 调用该方法需要传入的参数     * @return     */    public Object invoke2(Object obj, String methodName, Object ... args){        //1. 获取Method 对象        Class<?> [] parameterTypes = getParameterTypes(args);        try {            Method method = getMethod(obj.getClass(), methodName, parameterTypes);            return invokeMethod(obj, method, args);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 工具方法2:      */    @Test    public void testInvoke2(){        Object obj = new Student();        //其中 setName 是Student 父类Person 中的方法.        invoke2(obj, "setName", "huel", 10);    }

2. 关于Field

如何获取Field:

    /**     * Field: 封装了字段的信息     * 1. 获取字段:     * 1.1 Field [] fields = clazz.getDeclaredFields();     * 1.2 Field field = clazz.getDeclaredField("name");     *      * 2. 获取指定对象的指定字段的值.     * public Object get(Object obj)     * obj: 字段所在的对象.     *      * Object val = field.get(person);     * 注意: 若该字段是私有的, 需要先调用setAccessible(true)     *      * 3. 设置指定对象的指定字段的值.     * public void set(Object obj, Object value)     * obj: 字段所在的对象     * value: 要设置的值.     *      * field.set(person, "atguigu");     */    @Test    public void testField() throws Exception{        String className = "henan.huel.java.Person";        Class clazz = Class.forName(className);        //1. 获取字段        //1.1 获取Field 的数组        Field [] fields = clazz.getDeclaredFields();        for(Field field : fields){            System.out.println(field.getName());        }        //1.2 获取指定名字的Field        Field field = clazz.getDeclaredField("name");        System.out.println(field.getName());        Person person = new Person("huel", 21);        //2. 获取指定对象的Field 的值        Object val = field.get(person);        System.out.println(val);        //3. 设置指定对象的Field 的值        field.set(person, "atguigu");        System.out.println(person.getName());        //4. 若该字段是私有的, 需要调用setAccessible(true)        Field field2 = clazz.getDeclaredField("age");        field2.setAccessible(true);        System.out.println(field2.get(person));    }

工具方法:为对象的某个字段赋值

    /**     * 循环向上转型, 获取对象的 DeclaredField     * @param clazz: Class 对象clazz     * @param fieldName: 类的一个字段的字段名. 该字段可能是private 修饰, 还可能是在父类中定义的(私有) 字段.     * @return     */    public Field getField(Class<?> clazz, String fieldName){        for(Class<?> superClass = clazz; superClass != Object.class;                 superClass = superClass.getSuperclass()){            try {                Field field = superClass.getDeclaredField(fieldName);                return field;            } catch (Exception e) {            }        }        return null;    }    /**     * 设置指定对象的Field 的值     * @param obj: 字段所在的对象     * @param field: 哪一个字段?     * @param val: 要设置的值.     * @throws IllegalAccessException     */    private void setFieldValue(Object obj, Field field, Object val)            throws IllegalAccessException {        field.setAccessible(true);        field.set(obj, val);    }    /**     * 获取指定对象的Field 的值     */    public Object getFieldValue(Object obj, Field field)             throws IllegalArgumentException, IllegalAccessException{        field.setAccessible(true);        return field.get(obj);    }    /**     * 为对象的某个字段赋值     */    @Test    public void testClassField() throws NoSuchFieldException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException{        String className = "henan.huel.java.Student";        String fieldName = "age";        Object val = 21;        Object obj = null;        Class clazz = Class.forName(className);        Field field = getField(clazz, fieldName);        obj = clazz.newInstance();        setFieldValue(obj, field, val);        Student stu = (Student)obj;        System.out.println(stu.getAge());    }

3. Constructor:

如何获取Constructor:

    /**     * 关于Constructor:     * Constructor: 代表构造器     * 1. 获取Constructor 对象     * 2. 调用构造器 的 newInstance() 方法创建对象     */    @Test    public void testConstructor() throws Exception{        String className = "henan.huel.java.Person";        Class clazz = Class.forName(className);        //1. 获取Constructor 对象        Constructor<Person> [] constructors =                 clazz.getConstructors();        for(Constructor<Person> constructor : constructors){            System.out.println(constructor);        }        Constructor<Person> constructor =                clazz.getConstructor(String.class, int.class);        System.out.println(constructor);        //2. 调用构造器 的 newInstance() 方法创建对象        Object obj = constructor.newInstance("huel", 21);    }

4. Annotation

Annotation的简单示例:

/**     * 关于Annotation:     *      * @throws Exception     */    @Test    public void testAnnotation() throws Exception{        String className = "henan.huel.java.Person";        Class<Person> clazz = (Class<Person>) Class.forName(className);        Object obj = clazz.newInstance();        Method method = clazz.getDeclaredMethod("setAge", int.class);        int val = 30;        Annotation annotation = method.getAnnotation(AgeValidator.class);        if(annotation != null){            if(annotation instanceof AgeValidator)            {                AgeValidator ageValidator = (AgeValidator) annotation;                if(val < ageValidator.min() || val > ageValidator.max()){                    throw new RuntimeException("年龄非法");                }            }        }        method.invoke(obj, val);        System.out.println(obj);    }

5. 泛型与反射

工具方法:获取泛型参数的类型

    /**     * 通过反射, 获取定义Class 时声明的父类的泛型参数的类型     * 如: public EmployeeDao extends BaseDao<Employee, String>     * @param clazz: 子类对应的Class 对象     * @param index: 子类继承父类时传入的泛型的索引. 从0 开始.     * @return     */    public static Class<?> getSuperClassGenericType(Class<?> clazz, int index){        //获取BaseDao 子类的带泛型参数的父类        Type type = clazz.getGenericSuperclass();        //获取具体的泛型参数        if(type instanceof ParameterizedType){            ParameterizedType parameterizedType =                     (ParameterizedType) type;            Type [] args = parameterizedType.getActualTypeArguments();            if(args != null && index >=0 && index <= args.length-1){                Type arg = args[index];                if(arg instanceof Class){                    return (Class<?>) arg;                }            }        }        return null;    }    /**     * 获取泛型参数的类型     */    @Test    public void tesGetSuperClassGenericType(){        Class clazz = EmployeeDao.class;        Class argsClass = getSuperClassGenericType(clazz, 0);        System.out.println(argsClass);          argsClass = getSuperClassGenericType(clazz, 1);        System.out.println(argsClass);      }

总结: 这里只是对反射基础知识的总结,如果以后深入了解反射,还会进行更新。

0 0
原创粉丝点击