zoukankan      html  css  js  c++  java
  • 反射详解

    一、啥是反射?

      通过反射机制加载一个class字节码文件,获得并调用任意一个类的所有属性和方法。

    二、反射能干啥?

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的方法

    三、干一干试试看!

    1. Animal样本类

    public class Animal {
        public String name = "Dog";
        private int age = 3;
        public Animal(){
            System.out.println("Public-Animal");
        }
        public Animal(String name){
            System.out.println("Public-Animal:name"+name);
        }
        private Animal(String name, int age){
            System.out.println("Private-Animal:name:" + name + ",age:" + age );
        }
        public String sayName(String name){
            return "Hello," + name;
        }
    }

    2. ReflectTest测试类

    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            //获取Class对象的三种方式
            Class animal = Class.forName("com.reflect.Animal");
            Class animal_2 = Animal.class;
            Animal animalObj = new Animal();
            Class animal_3 = animalObj.getClass();
            System.out.println(animal);//class com.reflect.Animal
            System.out.println(animal_2);//class com.reflect.Animal
            System.out.println(animal_3);//class com.reflect.Animal
            System.out.println(animal == animal_2);//true
            System.out.println(animal == animal_3);//true
    
            //获得成员变量
            //1.获得所有声明的成员变量
            Field[] declaredFieldList = animal.getDeclaredFields();
            for(Field field : declaredFieldList){
                System.out.println("DeclaredField"+field);
                //DeclaredFieldpublic java.lang.String com.reflect.Animal.name
                //DeclaredFieldprivate int com.reflect.Animal.age
            }
            //2.获得所有共有的成员变量
            Field[] pubFieldList = animal.getFields();
            for(Field field : pubFieldList){
                System.out.println("PublicField" + field);
                //PublicFieldpublic java.lang.String com.reflect.Animal.name
            }
    
            //获得构造器
            //1.获得所有声明的构造器
            Constructor[] declaredConst = animal.getDeclaredConstructors();
            for(Constructor constructor : declaredConst){
                System.out.println(constructor);
                //private com.reflect.Animal(java.lang.String,int)
                //public com.reflect.Animal(java.lang.String)
                //public com.reflect.Animal()
            }
            //2.获得所有共有的构造器
            Constructor[] pubConst = animal.getConstructors();
            for(Constructor constructor : pubConst){
                System.out.println(constructor);
                //public com.reflect.Animal(java.lang.String)
                //public com.reflect.Animal()
            }
    
            //获得非构造方法,具体不演示了
            //1.获得所有声明非构造方法
            Method[] declaredMethodList = animal.getDeclaredMethods();
            //获得公有非构造方法
            Method[] methodList = animal.getMethods();
        }
    }

    3. 进阶测试类

    public class ReflectDemo {
        public static void main(String[] args) throws Exception {
            // 1.通过字符串获取Class对象,这个字符串必须带上完整路径名
            Class animalClass = Class.forName("com.reflect.Animal");
    
            // 2.获取声明的构造方法,传入所需参数的类名,如果有多个参数,用','连接即可, private Animal(String name, int age)
            Constructor animalConstructor = animalClass.getDeclaredConstructor(String.class, Integer.class);
            // 如果是私有的构造方法,需要调用下面这一行代码使其可使用,公有的构造方法则不需要下面这一行代码
            animalConstructor.setAccessible(true);
            // 使用构造方法的newInstance方法创建对象,传入构造方法所需参数,如果有多个参数,用','连接即可
            Object animal = animalConstructor.newInstance("Cat",100);
    
            // 3.获取声明的字段,传入字段名  private int age = 3;
            Field animalgeField = animalClass.getDeclaredField("age");
            // 如果是私有的字段,需要调用下面这一行代码使其可使用,公有的字段则不需要下面这一行代码
            animalgeField.setAccessible(true);
            // 使用字段的set方法设置字段值,传入此对象以及参数值
            animalgeField.set(animal,10);
    // 4.获取声明的函数,传入所需参数的类名,如果有多个参数,用','连接即可 Method studentShowMethod = animalClass.getDeclaredMethod("sayName", String.class); // 如果是私有的函数,需要调用下面这一行代码使其可使用,公有的函数则不需要下面这一行代码 //studentShowMethod.setAccessible(true); // 使用函数的invoke方法调用此函数,传入此对象以及函数所需参数,如果有多个参数,用','连接即可。函数会返回一个Object对象,使用强制类型转换转成实际类型即可 Object result = studentShowMethod.invoke(animal,"message"); System.out.println("result: " + result);//result: Hello,message,age:10
        }
    }

    四、为啥这么搞,不是更复杂了吗?

      为啥不直接使用当前类的方法属性,而是利用反射通过class文件获取呢?

      举例:Spring IOC容器进行Bean管理,在Spring中我们经常看到:

       Spring就是通过配置文件中class路径进行反射处理获得对应的对象。这样便可以根据不同的配置进行相应的处理。而不需要每次都要new一个,不把代码写死。

    //解析<bean .../>元素的id属性得到该字符串值为"sqlSessionFactory" 
            String idStr = "sqlSessionFactory";  
            //解析<bean .../>元素的class属性得到该字符串值为"org.mybatis.spring.SqlSessionFactoryBean"  
            String classStr = "org.mybatis.spring.SqlSessionFactoryBean";  
            //利用反射知识,通过classStr获取Class类对象  
            Class cls = Class.forName(classStr);  
            //实例化对象  
            Object obj = cls.newInstance();  
            //container表示Spring容器  
            container.put(idStr, obj);  
            
            //当一个类里面需要用另一类的对象时,我们继续下面的操作
            
            //解析<property .../>元素的name属性得到该字符串值为“dataSource”  
            String nameStr = "dataSource";  
            //解析<property .../>元素的ref属性得到该字符串值为“dataSource”  
            String refStr = "dataSource";  
            //生成将要调用setter方法名  
            String setterName = "set" + nameStr.substring(0, 1).toUpperCase()  
                    + nameStr.substring(1);  
            //获取spring容器中名为refStr的Bean,该Bean将会作为传入参数  
            Object paramBean = container.get(refStr);  
            //获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象  
            Method setter = cls.getMethod(setterName, paramBean.getClass());  
            //调用invoke()方法,此处的obj是刚才反射代码得到的Object对象  
            setter.invoke(obj, paramBean);  

    总结

    • 反射让开发人员可以通过外部类的全路径名创建对象,并使用这些类,实现一些扩展的功能。
    • 反射让开发人员可以枚举出类的全部成员,包括构造函数、属性、方法。以帮助开发者写出正确的代码。
    • 测试时可以利用反射 API 访问类的私有成员,以保证测试代码覆盖率。
  • 相关阅读:
    poj 2488 DFS
    畅通工程 并查集模版
    KMP 模板
    poj 1426 DFS
    poj 2528 线段数
    poj 3468 线段数 修改区间(点)
    CVPR2012文章阅读(2)A Unified Approach to Salient Object Detection via Low Rank Matrix Recovery
    如何制定目标
    Saliency Map 最新综述
    计算机视觉模式识别重要会议杂志
  • 原文地址:https://www.cnblogs.com/qmillet/p/12503090.html
Copyright © 2011-2022 走看看