反射详解

一、啥是反射?

  通过反射机制加载一个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 访问类的私有成员,以保证测试代码覆盖率。
原文地址:https://www.cnblogs.com/qmillet/p/12503090.html