zoukankan      html  css  js  c++  java
  • Java反射机制开发经验总结

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

    存在这样一个类:

    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");

    image

    注意一点,这里的forName("xxx")的类名需要全名,且为接口或类,否则加载不了。

    2 - User user = new User();

    Class clazz2 = user.getClass();

    image

    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打断点,可以看到——

    image

    这个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注解的内部属性有如下方式——
    image

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

    image

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

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

    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调用成功了。

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

    最后,通过反射实现一个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;
        }
    

    欢迎关注公众号,关于思考,关于文化,关于成长——

    image

    作者:朱季谦
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    451. Sort Characters By Frequency
    424. Longest Repeating Character Replacement
    68. Text Justification
    44. Wildcard Matching
    160. Intersection of Two Linked Lists
    24. Swap Nodes in Pairs
    93. 递归实现组合型枚举
    98. 分形之城
    97. 约数之和
    96. 奇怪的汉诺塔
  • 原文地址:https://www.cnblogs.com/zhujiqian/p/14979486.html
Copyright © 2011-2022 走看看