美文网首页程序员
【基础总结】反射机制

【基础总结】反射机制

作者: 灰色孤星 | 来源:发表于2018-10-21 21:37 被阅读3次

    源代码:https://gitee.com/AgentXiao/reflection
    反射的要点:
    1、反射的概念(是什么、有什么用)
    2、获得Class类对象
    3、使用反射操作基本方法、构造方法、属性
    4、反射的效率问题
    5、使用反射操作泛型和注解

    一、什么是反射机制

    1、指的是可以在运行时加载、探知、使用编译期间完全未知的
    2、程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
    3、加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
    4、java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身。
    5、当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。
    6、Class类是Reflection的根源。

    二、获得Class类的对象

    • getClass()
    • Class.forName()
    • .class
      注意:Class类的对象是针对类的,因此同一个类的Class类对象是相同的。
    /**
     * @ClassName Demo01
     * @Description 测试获得Class类的对象
     * @Author xwd
     * @Date 2018/10/19 16:13
     */
    public class Demo01 {
        public static void main(String[] args) {
            String computer = "pri.xiaowd.reflection.Computer";
    
            try {
                Class c1 = Class.forName(computer);
                Class c2 = new Computer().getClass();
                Class c3 = Computer.class;
    
                System.out.println(c1.hashCode());
                System.out.println(c2.hashCode());
                System.out.println(c3.hashCode());
    
                System.out.println(computer.getClass().hashCode() == String.class.hashCode());
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    以上代码的执行结果:

    测试反射机制

    三、获得类的基本信息

    /**
     * @ClassName Demo02
     * @Description 获得类的基本信息(类名、构造器、方法、属性)
     * @Author xwd
     * @Date 2018/10/19 16:36
     */
    public class Demo02 {
        public static void main(String[] args) {
            String computer = "pri.xiaowd.reflection.Computer";
    
            try {
                Class clazz = Class.forName(computer);
    
                //获得类名
                String className = clazz.getName();  //pri.xiaowd.reflection.Computer
                String classSimpleName = clazz.getSimpleName();  //Computer
    
                System.out.println(className);
                System.out.println(classSimpleName);
    
                //获得构造器
                //Constructor[] constructors = clazz.getConstructors();//只获得public修饰的构造器
                Constructor[] constructors = clazz.getDeclaredConstructors();//获得所有定义的构造器
                for(Constructor constructor:constructors){
                    System.out.println("构造器:"+constructor);
                }
                //考虑重载问题
                Constructor c1 = clazz.getConstructor(null);//获得空构造
                Constructor c2 = clazz.getConstructor(String.class,String.class);//获得带参构造
    
                //获得方法
                //Method[] methods = clazz.getMethods();//只获得public修饰的方法
                Method[] methods = clazz.getDeclaredMethods();//获得全部方法
                for(Method method:methods){
                    System.out.println("方法:"+method);
                }
                //考虑重载问题
                Method m1 = clazz.getMethod("getName",null);
                Method m2 = clazz.getMethod("setName",String.class);
    
                //获得属性
                //Field[] fields = clazz.getFields();//获得public修饰的属性
                Field[] fields = clazz.getDeclaredFields();//获得所有属性
                for(Field field:fields){
                    System.out.println("属性:"+field);
                }
                Field f1 = clazz.getDeclaredField("name");//获得name属性
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    四、通过反射实现动态操作基本方法、构造方法、属性

    /**
     * @ClassName Demo03
     * @Description 通过反射实现动态操作
     * @Author xwd
     * @Date 2018/10/19 17:13
     */
    public class Demo03 {
        public static void main(String[] args) {
            String computer = "pri.xiaowd.reflection.Computer";
    
            try {
                Class<Computer> clazz = (Class<Computer>) Class.forName(computer);
                //通过反射调用无参构造
                Computer c1 = clazz.newInstance();
                System.out.println(c1);
                //通过反射调用有参构造
                Constructor constructor = clazz.getConstructor(String.class,String.class);
                Computer c2 = (Computer) constructor.newInstance("001","xwd");
                System.out.println(c2.getId());
    
                //通过反射API调用普通方法
                Method method = clazz.getDeclaredMethod("setName", String.class);
                method.invoke(c1,"lsm");
                System.out.println(c1.getName());
    
                //通过反射API可以直接访问属性
                Field f = clazz.getDeclaredField("id");
                f.setAccessible(true);//不需要再进行安全检查,可以直接访问私有属性
                f.set(c1,"000001");
                System.out.println(c1.getId());
                System.out.println(f.get(c1));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    五、反射的效率

    使用setAccessible(true)禁用安全检查可以加快反射的效率。

    /**
     * @ClassName Demo04
     * @Description 测试反射的效率
     * @Author xwd
     * @Date 2018/10/20 20:52
     */
    public class Demo04 {
        /**
         * @MethodName test1
         * @Descrition 测试直接调用情况下的耗时
         * @Param []
         * @return void
         */
        public static void test1(){
            Computer c = new Computer();
    
            long start = System.currentTimeMillis();
            for(int i=0;i<1000000000L;i++){
                c.getId();
            }
            long end = System.currentTimeMillis();
            System.out.println("执行10亿次getId(),直接调用耗时:"+(end - start));
        }
        /**
         * @MethodName test2
         * @Descrition 使用反射机制调用,不禁用安全检查
         * @Param []
         * @return void
         */
        public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Computer c = new Computer();
            Class clazz = c.getClass();
            Method getName = clazz.getMethod("getName");
            long start = System.currentTimeMillis();
            for(int i=0;i<1000000000L;i++){
                getName.invoke(c,null);
            }
            long end = System.currentTimeMillis();
            System.out.println("执行10亿次getId(),使用反射机制调用,不禁用安全检查耗时:"+(end - start));
        }
        /**
         * @MethodName test3
         * @Descrition 使用反射机制调用,禁用安全检查
         * @Param []
         * @return void
         */
        public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Computer c = new Computer();
            Class clazz = c.getClass();
            Method getName = clazz.getMethod("getName");
            getName.setAccessible(true);
            long start = System.currentTimeMillis();
            for(int i=0;i<1000000000L;i++){
                getName.invoke(c,null);
            }
            long end = System.currentTimeMillis();
            System.out.println("执行10亿次getId(),使用反射机制调用,禁用安全检查耗时:"+(end - start));
        }
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            test1();
            test2();
            test3();
        }
    }
    

    控制台的输出信息为:

    测试反射的效率

    六、反射操作泛型

    Java采用 泛型擦除 的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
    因为反射是操作加载好的类的,因此通过反射是无法直接操作泛型的。

    • 为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
    /**
     * @ClassName Demo05
     * @Description 反射操作泛型
     * @Author xwd
     * @Date 2018/10/21 21:07
     */
    public class Demo05 {
    
        public void test01(Map<String,Computer> map, List<Computer> list){
            System.out.println("Demo05.test01()");
        }
    
        public Map<Integer,Computer> test02(){
            System.out.println("Demo05.test02()");
            return null;
        }
    
        public static void main(String[] args) {
            try {
                //获得指定方法的参数泛型信息
                Method m1 = Demo05.class.getMethod("test01",Map.class,List.class);
                Type[] types = m1.getGenericParameterTypes();//获得带泛型的参数类型
                for(Type type:types){
                    System.out.println(type);  //遍历参数类型
                    if(type instanceof ParameterizedType){
                        //获得真正的泛型信息
                        Type[] genericTypes = ((ParameterizedType)type).getActualTypeArguments();
                        for(Type genericType:genericTypes){
                            System.out.println("参数泛型类型:"+genericType);
                        }
                    }
                }
    
                System.out.println("*******************************");
    
                //获得指定方法的返回值泛型信息
                Method m2 = Demo05.class.getMethod("test02",null);
                Type returnTypes = m2.getGenericReturnType();//获得返回值的类型
                System.out.println(returnTypes);  //遍历参数类型
                if(returnTypes instanceof ParameterizedType){
                    //获得真正的泛型信息
                    Type[] genericTypes = ((ParameterizedType)returnTypes).getActualTypeArguments();
                    for(Type genericType:genericTypes){
                        System.out.println("返回值泛型类型:"+genericType);
                    }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }
    

    控制台输出信息:

    反射操作泛型

    七、反射操作注解

    详见【基础总结】注解Annotation

    相关文章

      网友评论

        本文标题:【基础总结】反射机制

        本文链接:https://www.haomeiwen.com/subject/dntuzftx.html