(十九)反射、内省机制

反射


什么是反射

在程序运行过程中,通过字节码对象,去获取类的信息(构造器,方法,字段)


为什么使用反射?

当我们知道要使用哪个类时,我们直接调用该类创建对象,调用方法即可,但是

当我们不知道使用哪个类时,我们可以通过反射去获取类的信息,使用反射获取到类的对象的真实类型以及调用其方法


什么是字节码对象?

把类想象成一个一个独立的个体,那么他们所共有的特性就是一个大Class来概括,把类抽象成一个一个对象,而字节码就是可以抽象成一个类,通过类去创建真实对象.

  • 正常类创建对象:
Person p =new Person();  //p是类Person的一个实例

把Class想象成一个装载所有类共性的类,那么我们需要创建字节码对象,通过这个对象去最终能够拿到person这个类里的实例数据

  • 字节码对象的创建
  1. 对象.getClass()
  2. 类.calss
  3. class.forName("类的全限定名"):会有异常 ClassNotFoundException:因为传入路径不知道是否真的存在
1.对象.getClass()
    Person p =new Person();
    //System.out.println(p.getClass()); 
     Class<? extends Person> aClass1 = p.getClass();
2..class
    //System.out.println(Person.calss);
     Class<People> peopleClass = People.class;
3.Class.forName("类的全限定名"); //全限定名:包名路径加类名
  Class<?> aClass = Class.forName("cn.k.day02_reflect.Person");

​ 三种创建方式,第三种最灵活,因为使用前两种,都是先定义好类再去通过其字节码进行反射

但是第三种,可以先进行创建字节码对象,再去定义类,顺序不同,所以第三种更优,并且可以获取任意对象的字节码


反射操作

使用反射的目的就是想去操作类中的成员,那么一个类中有构造器,方法,字段,所以要想操作它们,得有对应的对象,构造器对象,方法对象等.


获取构造器对象

如果要想获取构造器对象,那么首先得有字节码对象,通过字节码对象去获取构造器对象,一个字节码对应了一个对象. Class提供了对应的创建构造器的API

  1. 获取字节码对象
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
  • 获取所有公共构造器对象 getConstructors()
Constructor<?>[] constructors = aClass.getConstructors();
  • 获取所有的构造器对象(含私有) getDeclaredConstructors()
Constructor<?>[] constructors1 = aClass.getDeclaredConstructors();
  • 获取公共的无参构造器对象 getConstructor()
 Constructor<?> constructor = aClass.getConstructor();
  • 获取公共带参数的构造器对象 (带两个参数,传入参数是字节码类型)
Constructor<?> constructor2 = aClass.getConstructor(String.class, String.class);

通过构造器对象去获取真实对象

构造器.newInstance([参数]) :参数看构造器是有参(传入对应的数量)还是无参

// 这里使用的是使用无参构造器去创建对象 底层其实就是 new People()
Object o = constructor.newInstance();
字节码对象也可以直接获取真实对象

Class 提供了通过字节码对象直接获取真实对象的API 字节码对象.newInstance()

注意

如果直接通过class字节码对象获取它的一个实例class.newInstance(); 其使用必须在真实类中,存在一个公共的无参数的构造方法


获取方法对象

想要获取方法对象,首先还是得有字节码对象,再通过Class提供的API去创建方法对象

  1. 获取字节码对象
Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
  • 获取所有公共的方法对象(包括了父类)
Method[] methods = aClass.getMethods();
  • 获取所有的方法对象(不包括父类)
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
  • 获取指定方法的方法对象
Method getNameMethod = aClass.getMethod("getName");
  • 访问私有成员方法
Method printMethod = aClass.getDeclaredMethod("print",String.class);
printMethod.setAccessible(true); //开关 是否暴力访问 私有的成员
printMethod.invoke(o,"暴力访问");
执行方法invoke


传入使用字节码对象的newInstance()方法,构建真实对象    
//需要传入String.class因为 setName方法中有参数,对应的也要传入参数,不然谁知道是什么类型
Method setNameMethod = aClass.getMethod("setName", String.class); //获取setter方法
setNameMethod.invoke(aClass.newInstance(),"ssss");//设置set值

 Method getNameMethod = aClass.getMethod("getName");//获取getter方法
 Object invoke1 = getNameMethod.invoke(aClass.newInstance());//执行该对象的getter值

System.out.println("invoke1 = " + invoke1); 

    

结果 :打印为空
原因: 在执行方法时 ,setNameMethod.invoke 与 getNameMethod.invoke 传入的对象地址已经不一样了,相当于new了一个新的对象.

解决方案:应该把对象给提取出来,再去传入共同对象,或者使用构造器去获取真实对象

 Object o =aClass.newInstance();
 Method setNameMethod = aClass.getMethod("setName", String.class); //获取setter方法
 setNameMethod.invoke(o,"ssss");//设置set值
  Method getNameMethod = aClass.getMethod("getName");//获取getter方法
 Object invoke1 = getNameMethod.invoke(o);//执行同一对象

输出invoke1结果 :invoke1 = ssss


获取字段

反射不建议直接操作字段(直接操作字段违背了封装的原则)


JavaBean引入

反射不建议操作类中的私有成员,因为这违反了封装的原则

  • Javabean是java中最重要的一个可重用组件(减少代码重复)

  • 组件:做到了可重用性,完成特定的功能

javabean规范
  1. 使用public修饰
  2. 字段私有化
  3. 提供get /set方法
  4. 公共的无参数的构造器
javabean三大成员
  1. 事件(基本不用了)
  2. 方法
  3. 属性

什么是属性(property)?

属性不是字段,property只关心getter或者setter,不关心定义,只要有getter或者setter其中一个就算是 属性 ,属性是通过getter,setter推断出来的

只要满足一个类中,有public修饰 ,有公共无参构造器,有getter或者setter其中一个方法,就算是满足了JavaBean

那么从而引入了一个机制 ,当我们去访问私有成员时,我们可以内省机制来去获取javabean中的方法


内省机制

内息的核心类 :Introspector

目的:获取JavaBean中的方法与属性

步骤
  1. 获取字节码对象,获取到到javabean的描述对象 Introspector.getBeanInfo(aClass, Object.class)
  2. 通过描述对象,获取到属性描述器 PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
  3. 通过属性描述器,来获取到属性名getName(), 读方法getReadMethod(),写方法getWriteMethod() ,读(getter),写(setter)


操作

public class BeanInfoDemo {
    public static void main(String[] args) throws Exception {
        //反射不建议直接操作字段,可以通过内省机制通过getter,setter操作
        Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
        //获取构造器对象
        Constructor<?> constructor = aClass.getConstructor();
        //获取构造器中的实例
        Object o = constructor.newInstance();
        //获得描述对象
        BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
        //获得getter,setter的一个属性数组

        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            //获取这个方法 (set)
            Method writeMethod = propertyDescriptor.getWriteMethod();
            //得到属性 (get)
            Method readMethod = propertyDescriptor.getReadMethod();
            //获取属性名字
            String name = propertyDescriptor.getName();
            //如果属性名字等于name
            if (name.equals("name")){
                //设置
                writeMethod.invoke(o,"小月");
                //获取设置值
                Object invoke = readMethod.invoke(o);
                System.out.println("invoke = " + invoke);
            }

        }

    }
}


JavaBean与Map间的转换

javabean 转换 map
public static Map<String,Object> javaBeanToMap(Class aClass ,Object o) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
       

        //获取javabean描述对象
        BeanInfo beanInfo = Introspector.getBeanInfo(aClass, Object.class);
        //获取属性描述器
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        //遍历获得属性
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            //获取key
            String name = propertyDescriptor.getName();
            //获取value
            Method readMethod = propertyDescriptor.getReadMethod();
            //执行
            Object value = readMethod.invoke(o);
            map.put(name,value);

        }
        return map;

    }
map 转换 javabean
 public static <T> T MapToJavaBean(Map<String,Object> map ,Class<T> cas) throws Exception {
        T t = cas.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(cas, Object.class);
        //获取属性描述器
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        //遍历获得属性
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            //获取key
            String name = propertyDescriptor.getName();
            //获取value
            Object value = map.get(name);
             //set进去
            Method writeMethod = propertyDescriptor.getWriteMethod();
            writeMethod.invoke(t,value);

        }
        return t;

    }
  • test类
public class BeanMapDemo {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("cn.k.day02_reflect.People");
        People p = new People("轩爷", 22, "男");
        Map<String, Object> map = javaBeanToMap(aClass,p);
        System.out.println(map);
        People people = MapToJavaBean(map, People.class);
        System.out.println("people = " + people);

    }
  }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值