当前位置 博文首页 > 程序员漫话编程的博客:码神总结的Java反射机制经验,太精辟了!

    程序员漫话编程的博客:码神总结的Java反射机制经验,太精辟了!

    作者:[db:作者] 时间:2021-09-18 18:58

    原创不易,有用就关注一下。要是帮到了你 就给个三连吧,多谢支持。


    ### >>三期资源-3T免费视频资料。
    ?

    我在实际项目当中有经常用到反射机制,故而将学会的反射用法做一些汇总笔记,当做以后复盘所用。

    存在这样一个类:

    package com.example.demo;
    import com.alibaba.fastjson.annotation.JSONField;
    public class User {
        private String name;
        @Value( value ="age_a")
        private String age;
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
         public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    }

    一、创建Class的三种方式1 - Class clazz = Class.forName("com.example.demo.User");注意一点,这里的forName("xxx")的类名需要全名,且为接口或类,否则加载不了。

    2 - User user = new User();Class clazz2 = user.getClass();3 - Class clazz3 = User.class;以上三种方式,都可以获取到类User的Class对象,通过Class,即可以开始玩反射了。

    二、反射获取类的所有属性和属性类型Class clazz = User.class;
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
    ? ?System.out.println("属性名:

    "+field.getName());
    ? ?System.out.println("属性的类型:

    "+field.getGenericType().getTypeName());
    }
    打印输出User的属性和属性类型——属性名:

    name属性的类型:

    java.lang.String属性名:

    age属性的类型:

    java.lang.String利用反射获取到类的字段属性后,是不是可以利用反射来创建一个对象呢?

    答案是肯定的。

    例如,可以类似下面代码,通过反射得到的字段属性,进而创建一个对象。

    Map<String,Object> fileds = new HashMap<>();
    fileds.put("name","张三");
    fileds.put("age","10");
    Object o = User.class.newInstance();
     Field[] fields = o.getClass().getDeclaredFields();
     for (Field field : fields) {
         //设置后可用反射访问访问私有变量
         field.setAccessible(true);
         //通过反射给属性赋值
         field.set(o,fileds.get(field.getName()));
     }
     User user1 = (User) o;
     System.out.println(user1.toString());

    什么场景下可能需要这样做的呢?

    像一些内部数据与外部数据字段的映射,就可以通过类似的字段反射方式,将源数据映射给目标数据,进而得到可以插入数据库的目标对象。

    三、反射动态修改类属性的注解值注意一点,我们在设置User类时,对其中一个字段加了注解:

    @Value( value ="age_a")。

    这是一种设置值的注解,既然是设置值,是否还可以在代码运行过程中,根据不同情况来动态修改呢?

    字段上的注解,其实都存放在一个memberValues属性里,这是一个map,可以这样来获取——

    Field[] fields = User.class.getDeclaredFields();
    for (Field field : fields) {
        //设置后可用反射访问访问私有变量
        if ("age".equals(field.getName() )){
            field.setAccessible(true);
           //获取 annotation 这个代理实例所持有的 InvocationHandler
           InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
           // 获取 InvocationHandler 的 memberValues 字段
            Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
            memberValues.setAccessible(true);
            Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
            System.out.println(values);
        }
    }

    debug打断点,可以看到——图片这个Map<String,Object>存储的是该@注解里的所有属性值,这里,@Value只有一个value属性——public @interface Value {
    ? ?String value();
    }
    若把它换成类似@JSONField(name="age_a"),把上边的代码稍微修改下,如:

    Field[] fields = User.class.getDeclaredFields();
    for (Field field : fields) {
        if ("age".equals(field.getName() )){
            field.setAccessible(true);
              InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(JSONField.class));
        ......
        }
    }

    @JSONField注解的内部属性有如下方式——再运行刚刚的代码,可以看到,这里Map<String,Object>获取存储到的,便是这个注解里所有的属性与对应的属性值。

    图片到了这一步,回到先前上边的问题,若要动态改变这个注解的值,怎么处理呢?

    其实,很简单,只需要直接进行值设置就可以了,例如——
    ?

    InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
    Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
    memberValues.setAccessible(true);
    Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
    values.put("value","new_age");
    memberValues.setAccessible(false);


    只是,注意一点是,这里的key需要对应上注解里是属性值。

    四、反射获取类的方法及调用方式 Object o=User.class.newInstance();
    //通过反射获取到User的setAge方法,后面的String.class表示这个setAge方法的参数类型,若有多个,则按顺序列出
    //同时,若为其他类型,如List,Long,则为List.class,Long.class

    Method m =  (Method) o.getClass().getMethod("setAge",String.class);
     m.invoke(o,"name");
     User user = (User) o;
     System.out.println(user);

    打印可见,age已为name,说明setAge调用成功了。

    这类使用场景,在代理当中出现比较多。

    最后,通过反射实现一个Map转成对象的封装工具—— ??

    public Object MapToObject(Object object,Map<String, Object> map) throws IllegalAccessException {
          Class cla =  object.getClass();
          Field[] fields = cla.getDeclaredFields();
          for(Field field:fields){
              field.setAccessible(true);
              if("serialVersionUID".equals(field.getName()))continue;
              if(map.get(field.getName())!=null) {
                  Object value=map.get(field.getName());
                  value=convertValType(value,field.getType());
                  field.set(object, value);
              }
          }
          return object;
      }
    
      private static Object convertValType(Object value, Class<?> fieldTypeClass) {
          Object o = null;
          if (Long.class.getName().equals(fieldTypeClass.getName())
                  || long.class.getName().equals(fieldTypeClass.getName())) {
              o = Long.parseLong(value.toString());
          } else if (Integer.class.getName().equals(fieldTypeClass.getName())
                  || int.class.getName().equals(fieldTypeClass.getName())) {
              o = Integer.parseInt(value.toString());
          } else if (Float.class.getName().equals(fieldTypeClass.getName())
                  || float.class.getName().equals(fieldTypeClass.getName())) {
              o = Float.parseFloat(value.toString());
          } else if (Double.class.getName().equals(fieldTypeClass.getName())
                  || double.class.getName().equals(fieldTypeClass.getName())) {
              o = Double.parseDouble(value.toString());
          } else {
              retVal = o;
          }
          return retVal;
      }

    关注公众号【程序员漫话编程】,后台回复?”学习资料“?,即可获得3T免费视频资源。

    原创不易,有用就关注一下。要是帮到了你 就给个三连吧,多谢支持。
    ?

    觉得不错的小伙伴,记得帮我?点个赞和关注哟,笔芯笔芯~**

    作者:码工
    ?

    有问题请留言或者私信,可以 微信搜索:程序员漫话编程,关注公众号获得更多免费学习资料。

    cs